aboutsummaryrefslogtreecommitdiffstats
path: root/code/unix
diff options
context:
space:
mode:
Diffstat (limited to 'code/unix')
-rwxr-xr-xcode/unix/ChangeLog2981
-rwxr-xr-xcode/unix/Cons_gcc.pm47
-rwxr-xr-xcode/unix/Conscript-client265
-rwxr-xr-xcode/unix/Conscript-dedicated115
-rwxr-xr-xcode/unix/Conscript-pk3135
-rwxr-xr-xcode/unix/Conscript-sdk122
-rwxr-xr-xcode/unix/Conscript-setup33
-rwxr-xr-xcode/unix/LinuxSupport/CHANGES-1.32.txt150
-rwxr-xr-xcode/unix/LinuxSupport/INSTALL52
-rwxr-xr-xcode/unix/LinuxSupport/index.html283
-rwxr-xr-xcode/unix/LinuxSupport/udp_wide_README.txt20
-rwxr-xr-xcode/unix/LinuxSupport/udp_wide_broadcast.patch57
-rwxr-xr-xcode/unix/Makefile2119
-rwxr-xr-xcode/unix/Makefile.Game285
-rwxr-xr-xcode/unix/Quake3.kdelnk12
-rwxr-xr-xcode/unix/README.EULA184
-rwxr-xr-xcode/unix/README.Linux352
-rwxr-xr-xcode/unix/README.Q3Test306
-rwxr-xr-xcode/unix/build_setup.sh125
-rwxr-xr-xcode/unix/cons6828
-rwxr-xr-xcode/unix/extract_ver.pl9
-rwxr-xr-xcode/unix/ftol.nasm151
-rwxr-xr-xcode/unix/linux_common.c344
-rwxr-xr-xcode/unix/linux_glimp.c1780
-rwxr-xr-xcode/unix/linux_joystick.c207
-rwxr-xr-xcode/unix/linux_local.h49
-rwxr-xr-xcode/unix/linux_qgl.c4153
-rwxr-xr-xcode/unix/linux_signals.c61
-rwxr-xr-xcode/unix/linux_snd.c293
-rwxr-xr-xcode/unix/matha.s425
-rwxr-xr-xcode/unix/pcons-2.3.17911
-rwxr-xr-xcode/unix/q3test.spec.sh52
-rwxr-xr-xcode/unix/qasm.h480
-rwxr-xr-xcode/unix/quake3.gifbin0 -> 1378 bytes
-rwxr-xr-xcode/unix/quake3.xpm161
-rwxr-xr-xcode/unix/run-target.sh12
-rwxr-xr-xcode/unix/snapvector.nasm95
-rwxr-xr-xcode/unix/snd_mixa.s217
-rwxr-xr-xcode/unix/sys_dosa.s115
-rwxr-xr-xcode/unix/unix_glw.h38
-rwxr-xr-xcode/unix/unix_main.c1273
-rwxr-xr-xcode/unix/unix_net.c616
-rwxr-xr-xcode/unix/unix_shared.c435
-rwxr-xr-xcode/unix/vm_x86.c29
-rwxr-xr-xcode/unix/vm_x86a.s462
45 files changed, 33839 insertions, 0 deletions
diff --git a/code/unix/ChangeLog b/code/unix/ChangeLog
new file mode 100755
index 0000000..2642f2d
--- /dev/null
+++ b/code/unix/ChangeLog
@@ -0,0 +1,2981 @@
+TODO: the Mac port seems stable, but outputs a bunch of warnings..
+
+2004-05-22
+- updated the xcode project from Apple's version
+ now with the latest vm_ppc code
+
+2004-05-21 Timothee Besset <ttimo@idsoftware.com>
+- fixed the Linux build to compile again on sid (glext.h and gcc3 warnings)
+- 2 weeks ago, hacked up the source to compile on panther / xcode 1.1
+ several cleanups were needed, and VM support seems broke (hangs or crashes)
+- got altivec optimisations from Apple (Kenneth Dyke)
+ merged back in
+- looks like with the new code merge the VM support is back in and working
+
+2003-09-15 Timothee Besset <ttimo@idsoftware.com>
+- import Q3 java master code, cleanups on monster
+
+2003-08-31 Timothee Besset <ttimo@idsoftware.com>
++ loki_setup hell
+ https://zerowing.idsoftware.com/bugzilla/show_bug.cgi?id=626
+ http://zerowing.idsoftware.com/linux/q3a/index.html#glibc
+ text mode installer in loki_setup image built on Mandrake 7.2 crashes on some glibc 2.3 systems such as RH9 etc.
+ need to move to a different version of the installer, and update old installers to keep them still 'installing'
+ moving to build the setup binaries on Debian Woody systems (glibc 2.2, text mode installer will no longer work on 2.1 systems)
+ hacked together a new setup, using setup tree from RTCW. would need a complete revamp if a new full setup with new binaries is needed
+
+2003-07-17 Timothee Besset <ttimo@idsoftware.com>
+ + new cvsreport, testing per-module config
+
+2003-01-19 Timothee Besset <ttimo@idsoftware.com>
+ + building on both gcc 2.x and 3.x
+ added conf modules to check gcc version
+ ccache support
+
+2003-01-13 Timothee Besset <ttimo@idsoftware.com>
+ + tweaking around for gcc 3.x build
+ edit Conscript to change the compiler
+
+2002-12-16 Timothee Besset <ttimo@idsoftware.com>
+ + added pbEmit class to auth code, emit CD keys to local PB master
+
+2002-11-14 Timothee Besset <ttimo@idsoftware.com>
+ + up to latest makeself.sh
+ + add both quake3.x86 and quake3-smp.x86 to setup
+ + https://zerowing.idsoftware.com/bugzilla/show_bug.cgi?id=573
+ console setup crash / glibc 2.3 (Debian Sid)
+ investigated, put together a workaround
+
+2002-11-5 Timothee Besset <ttimo@idsoftware.com>
+ + Linux building both smp and non-smp again. Will have to put both in setup
+ + added in_subframe to toggle X subframe event handling
+ + reworked the timing code to be more reliable
+ + cleaned up dgamouse/in_mouse code, removed unnecessary dgamouse var
+ + made the mouse grabbing an in_nograb cvar, no longer a compile time option
+ in_nograb 1 force in_dgamouse 0 and r_fullscreen 0 (any of those two will b0rk)
+ + https://zerowing.idsoftware.com/bugzilla/show_bug.cgi?id=565
+ mouse issues on Suze 8.1 - related to subframe event timing
+ added code chunk to detect broken X timing and disable subframe
+ + tweaked the subframe/X bug workaround to be less paranoid
+
+2002-10-28 Timothee Besset <ttimo@idsoftware.com>
+ + no longer blocking demo recording if g_synchronous clients != 0
+ only sending out a warning (everyone does g_sync 1 ; record ; g_sync 0)
+
+2002-10-21 Timothee Besset <ttimo@idsoftware.com>
+ + building final mod sdk setups (added lcc bins, added link to q3asm-turbo in readme)
+
+2002-10-8 Timothee Besset <ttimo@idsoftware.com>
+ + quickfix cl_maxpackets > 125 brings back to 100
+
+1.32 release ---
+
+2002-10-7 Timothee Besset <ttimo@idsoftware.com>
+ + made the 'demo' command case-insensitive on extension match (it was confused by demo FOUR.DM_68)
+ + mouse wheel scrolling with in_mouse 1 + window mode was not working, fixed (DI didn't catch)
+ + removing on-the-fly pk3 build from Linux setup, using the finalized ones now
+ added 'pk3' option to cons for toggle of pk3 building
+
+2002-10-5 Timothee Besset <ttimo@idsoftware.com>
+ + updated win32 mod sdk (in win32/mod-sdk-setup)
+ added q3asm and lcc source
+ updated the .bat to build VMs
+
+2002-10-3 Timothee Besset <ttimo@idsoftware.com>
+ + linux mod sdk, wrote the bulk of the scripts
+
+2002-9-30 Timothee Besset <ttimo@idsoftware.com>
+ + ATVI Quake 3 1.32 Patch #9
+ rolling back to the way it was before, leaving 1v1 force vote exploit, the fix was worse than the bug
+ from comment on bug #9 in tracker:
+
+ actually the fix is worse than the original bug
+
+ after the fix, voting when you are alone on the server was no longer working
+ it was kinda intended in the fix, that you would have to be at least two to pass a vote .. but
+ it is an oversight.
+
+ calling a vote in a 1v1 game against a bot fails immediately
+ (calling a vote in any situation where there's only 1 live player fails)
+
+ Say a server's running some lame custom map that you have but a friend doesn't. You can't go
+ on the server and change it to the map you want to play, so he ends up having to auto-dl it at
+ 8K a second just so you can switch from it.
+
+ This particular 2 clients, vote / quit exploit would involve too many changes to fix properly.
+ I am reverting back to the old version, and leaving as WNF
+
+2002-9-29 Timothee Besset <ttimo@idsoftware.com>
+ + https://zerowing.idsoftware.com/bugzilla/show_bug.cgi?id=50
+ added Wheel support to the DirectInput code IN_DIMouse (in_mouse 1)
+ tweaked the Wheel mouse reading for in_mouse -1 (old win32 input code)
+ handle correctly when zDelta is > 120
+ provide a in_logitechbug cvar to handle buggy Logitech MouseWare driver sending wheel events twice
+
+2002-9-26 Timothee Besset <ttimo@idsoftware.com>
+ + ATVI Quake 3 1.32 Patch #38
+ adding trap_SetPbClStatus, reliably checks for PB presence before enabling PB in UI
+
+2002-9-25 Timothee Besset <ttimo@idsoftware.com>
+ + https://zerowing.idsoftware.com/bugzilla/show_bug.cgi?id=551
+ SVF_CLIENTMASK, fixed a typo
+ + https://zerowing.idsoftware.com/bugzilla/show_bug.cgi?id=555
+ pushed cl_maxpackets upper limit to 125 (from 100) per CPMA Arqon's request
+
+2002-9-24 Timothee Besset <ttimo@idsoftware.com>
+ + ATVI Quake 3 1.32 Patch #33
+ PB reporting sv_paused cvar hacked, fixed SV_CheckPaused to use a Cvar_Set
+ + ATVI Quake 3 1.32 Patch #24
+ added [skipnotify] from RTCW, use to display in the console only, but not on client screen
+ (also fixes pb_msgprefix and pb_sv_msgprefix)
+ + https://zerowing.idsoftware.com/bugzilla/show_bug.cgi?id=553
+ using correct error message if listen server starting as cl_punkbuster 0 sv_punkbuster 1
+ + ATVI Quake 3 1.32 Patch #35
+ text auto wrap in UI code was eating the last word if it was wrapping
+ fixed in Q3 and TA UI (this bug could have affected the server print message also)
+ + some updates to the win32 cons post-build process
+
+2002-9-21 Timothee Besset <ttimo@idsoftware.com>
+ + adding bspc cons build script
+
+2002-9-19 Timothee Besset <ttimo@idsoftware.com>
+ + https://zerowing.idsoftware.com/bugzilla/show_bug.cgi?id=552
+ disconnect reason is transmitted in the disconnect command and processed into com_errorMessage
+ (similar to RTCW behaviour)
+ added UI for com_errorMessage cvar in baseq3/, if client is kicked/dropped/disconnected for whatever reason
+ (this is already functional in TA)
+ + ATVI Quake 3 1.32 Patch #9
+ failing vote if there's only one voting client (fixes exploit on 2-player server where one player votes then disconnects, forcing vote to pass)
+ + ATVI Quake 3 1.32 Patch #5
+ removed the userInfoChanged message (was a debugging leftover)
+ + ATVI Quake 3 1.32 Patch #18
+ rcon was not properly fixed yet, this only showed up for PB commands
+ changed the rcon parsing again to be more reliable
+
+2002-9-18 Timothee Besset <ttimo@idsoftware.com>
+ + https://zerowing.idsoftware.com/bugzilla/show_bug.cgi?id=549
+ the demo command has a list of compatible protocols, it will loop through 66 67 68
+ you can do '/demo four' and it will try four.dm_66 four.dm_67 four.dm_68
+ or you can explicitely give a '/demo demoname.dm_??'
+ + https://zerowing.idsoftware.com/bugzilla/show_bug.cgi?id=551
+ added SVF_CLIENTMASK (0x00000002), works only with <= 32 players
+ set bitmask of players to which send entity
+
+2002-9-17 Timothee Besset <ttimo@idsoftware.com>
+ + https://zerowing.idsoftware.com/bugzilla/show_bug.cgi?id=550
+ rcon bug fix
+ + some scons updates for win32 (post build)
+ + 1.32rc2
+
+2002-9-06 Timothee Besset <ttimo@idsoftware.com>
+ + updated completely the setup system:
+ fixed cons stuff to build setup with cons -- release setup
+ working from new setup codebase with some custom patches:
+ https://bugzilla.icculus.org/show_bug.cgi?id=52
+ https://bugzilla.icculus.org/show_bug.cgi?id=53
+ checked that BSD support was still in (brandelfing and symlinks) .. will have to get tester feedback
+ bumped version to 1.32rc1
+ TODO: update the windows .VCT (standalone setup and auto-update)
+
+2002-9-04 Timothee Besset <ttimo@idsoftware.com>
+ + https://zerowing.idsoftware.com/bugzilla/show_bug.cgi?id=543
+ backport from RTCW 1.4 code
+ rcon commands where sent after being tokenized and rebuilt
+ that was breaking any quoting, for instance 'rcon g_motd "hooka pooka"'
+ added Cmd_Cmd() to retrieve the un-tokenized command and transmit as is on both ends
+ + https://zerowing.idsoftware.com/bugzilla/show_bug.cgi?id=542
+ b0rked text wrapping in connect screen
+ was a missing sizeScale in q3_ui/, and a bad param in ui/
+ + https://zerowing.idsoftware.com/bugzilla/show_bug.cgi?id=540
+ backport fix to pk3 reordering, happens when clearing the references, bad order from connection may break stuff
+ + https://zerowing.idsoftware.com/bugzilla/show_bug.cgi?id=527
+ TA ui/, quickfix to netSource (mod stuff, doesn't affect TA)
+ + cleaned up broken old DO_WIN32 stuff in cons scripts
+ + https://zerowing.idsoftware.com/bugzilla/show_bug.cgi?id=526
+ typo in models2.shader
+ + https://zerowing.idsoftware.com/bugzilla/show_bug.cgi?id=443
+ Linux client: sub-frame timing of key/mouse events
+ + https://zerowing.idsoftware.com/bugzilla/show_bug.cgi?id=453
+ added mousewheel support: wheel to scroll, ctrl+wheel to scroll faster, shift+wheel to scroll history
+ + https://zerowing.idsoftware.com/bugzilla/show_bug.cgi?id=545
+ bumped server count to 4096
+ + keep around: __asm__ __volatile__ ("int $0x03");
+ + https://zerowing.idsoftware.com/bugzilla/show_bug.cgi?id=516
+ moved screenshots to backend with a new RC_SCREENSHOT render command
+ fixes the r_smp 1 garbled screenshots
+
+2002-8-29 Timothee Besset <ttimo@idsoftware.com>
+ + https://zerowing.idsoftware.com/bugzilla/show_bug.cgi?id=539
+ new VM code from Raven's Sof2
+ + cons / qvmtools build system fixes
+ + had to get a new qe3.ico again (resource compiler error)
+ http://vasin.hypermart.net/eei.htm
+ + updated, basic testing on win32, merging back in trunk
+ + merged bug-539 branch back into trunk, officialize the new VM code
+
+2002-8-26 Timothee Besset <ttimo@idsoftware.com>
+ + https://zerowing.idsoftware.com/bugzilla/show_bug.cgi?id=472
+ linux client: handle ctrl+space situations (could leave space locked on + space not working with ctrl on)
+ + update the build system, build q3lcc and q3asm etc. on demand
+ + https://zerowing.idsoftware.com/bugzilla/show_bug.cgi?id=62
+ fixed invisible players/entities
+
+2002-8-23 Timothee Besset <ttimo@idsoftware.com>
+ + https://zerowing.idsoftware.com/bugzilla/show_bug.cgi?id=536
+ fixing donedl being ignored after autodl if map_restart'ed (propagate from RTCW)
+ ignoring multiple map_restart (propagated from RTCW)
+ + reworked the server 'client text ignored' message to only trigger when there's actually a message that doesn't get to the game VM
+
+2002-8-18 Timothee Besset <ttimo@idsoftware.com>
+ + https://zerowing.idsoftware.com/bugzilla/show_bug.cgi?id=528
+ ydnar: reorg bits in the drawsurf sort index, push MAX_SHADERS to 2^12
+ + commented out some debug stuff in java auth server
+ + added FAQ item with Linux & BSD patch to handle broadcast on multiple interfaces
+
+2002-8-15 Timothee Besset <ttimo@idsoftware.com>
+ + https://zerowing.idsoftware.com/bugzilla/show_bug.cgi?id=534
+ fixing rcon being broken on NT/XP with > 23 days uptime (or so)
+ + https://zerowing.idsoftware.com/bugzilla/show_bug.cgi?id=525
+ changed the rcon buffer size to avoid overflows and dropping part of the message
+
+2002-8-14 Timothee Besset <ttimo@idsoftware.com>
+ + hacked in some experimental win32 stuff to the cons files
+ (win32 recognition and pk3 installs .. very very experimental but I needed it for win32 dev)
+ + https://zerowing.idsoftware.com/bugzilla/show_bug.cgi?id=521
+ ui/ and q3_ui/ : added text auto wrapping in the connection screen drawing (server message)
+ + https://zerowing.idsoftware.com/bugzilla/show_bug.cgi?id=531
+ removed the MPlayer stuff from the server browsers
+ + https://zerowing.idsoftware.com/bugzilla/show_bug.cgi?id=505
+ enabled back the ignore if protocol is != (fixes Wolf servers showing in browser)
+
+2002-8-10 Timothee Besset <ttimo@idsoftware.com>
+ + https://zerowing.idsoftware.com/bugzilla/show_bug.cgi?id=500
+ propagated IP banning fix from RTCW
+
+2002-8-08 Timothee Besset <ttimo@idsoftware.com>
+ + propagate additional sv_lanForceRate fix from RTCW
+
+2002-8-07 Timothee Besset <ttimo@idsoftware.com>
+ + added trap_FS_Seek
+
+2002-8-05 Timothee Besset <ttimo@idsoftware.com>
+ + https://zerowing.idsoftware.com/bugzilla/show_bug.cgi?id=50
+ fixed the DI mouse init procedure
+
+2002-8-05 Timothee Besset <ttimo@idsoftware.com>
+ + removed sv_allowanonymous, was dummy and polluting the serverinfo
+ (sv_allowanonymous was designed to flag wether server was public or not, but that's replaced by g_needpass)
+ + https://zerowing.idsoftware.com/bugzilla/show_bug.cgi?id=514
+ sv_strictAuth (default 1): server variable to control wether strict CDKEY auth should be performed
+ this is required if you want reliable cl_guid on the server
+ extended the getIpAuthorize (server->auth message) syntax
+ sending the fs_game at all times (default 'baseq3'), dummy sv_allowAnonymous 0, strict auth flag
+ NOTE: 1.31 server on baseq3 sends a getIpAuthorize packet like:
+ processing packet: getIpAuthorize -1230824753 217.128.77.195 0
+ the auth server will mistakenly read fs_game as '0'
+ + TAGGED the master / auth source as pre-1_32
+ will need to go back to this to comment out all my debugging crap
+
+2002-8-04 Timothee Besset <ttimo@idsoftware.com>
+ + cleaned master server stuff, client was prompting master.quake3arena.com,
+ server was sending heartbeats to master3.idsoftware.com
+ both point to 192.246.40.56, unified to master.quake3arena.com
+ the MPlayer master, master.quake3world.com doesn't exist anymore, switched it to master.quake3arena.com
+
+2002-8-02 Timothee Besset <ttimo@idsoftware.com>
+ + added auth server source, reorganized
+ + auth server name / master key optionally set on command line for master and auth servers
+ + auth and master config in build system
+ + https://zerowing.idsoftware.com/bugzilla/show_bug.cgi?id=524
+ changed default GL driver from libGL.so to libGL.so.1
+ see LSB 1.2 spec: http://www.linuxbase.org/spec/refspecs/LSB_1.2.0/gLSB/libgl.html
+ + https://zerowing.idsoftware.com/bugzilla/show_bug.cgi?id=480
+ applying the 'no cp command' experimental fix for beta phase
+ + https://zerowing.idsoftware.com/bugzilla/show_bug.cgi?id=462
+ backported from RTCW, fix to packet fragmenting emission
+ FIXME: there is some verbose code that we have to take out in the final version (grep for #462)
+ + https://zerowing.idsoftware.com/bugzilla/show_bug.cgi?id=475
+ backported from RTCW, don't get dropped if the server changes map while connecting (ignore outdated cp)
+ + PROTOCOL BUMPED TO 68
+
+2002-8-01 Timothee Besset <ttimo@idsoftware.com>
+ + Linux: dedicated build was not setting up signal handler like the full client does
+ + https://zerowing.idsoftware.com/bugzilla/show_bug.cgi?id=522
+ SplashDamage bugfix, now clearing client gentity before GAME_INIT call (instead of after)
+ + https://zerowing.idsoftware.com/bugzilla/show_bug.cgi?id=498
+ fixed NET_AdrToString to print the port as unsigned int (for ports > 1^^15, was showing negative)
+ + https://zerowing.idsoftware.com/bugzilla/show_bug.cgi?id=501
+ maintain IP in userinfo sent to game
+ + checking in master server source
+
+2002-7-31 Timothee Besset <ttimo@idsoftware.com>
+ + https://zerowing.idsoftware.com/bugzilla/show_bug.cgi?id=513
+ https://zerowing.idsoftware.com/bugzilla/show_bug.cgi?id=506
+ porting fix from RTCW codebase. client re-orders it's pk3s to scan in the same order than the server
+ this eliminates several 'Invalid .PK3 file referenced' situations (caused by client not referencing the same thing as server)
+ + fixed border remnants in ta ui
+ + https://zerowing.idsoftware.com/bugzilla/show_bug.cgi?id=517
+ ERR_DROP if PB client off / server on conflict when starting local server
+ + quickfix to q3 ui / punkbuster detect in server browser
+ + https://zerowing.idsoftware.com/bugzilla/show_bug.cgi?id=458
+ code fix, no more taunt spam
+ + cons install of PB .so files
+ + correct MOD_KAMIKAZE and MOD_JUICED in TA games.log
+
+2002-7-29 Timothee Besset <ttimo@idsoftware.com>
+ + q3 ui: completed confirmation prompts and messages (added UI_ConfirmMenu_Style & UI_Message)
+ + ta ui: backported 'conditionalopen' from RTCW (conditionalopen <cvar> <menu1> <menu2>)
+ + ta ui: confirmation prompt for punkbuster enable/disable etc.
+ + added the win32 DLLs to pb/win32/
+
+2002-7-28 Timothee Besset <ttimo@idsoftware.com>
+ + ta ui: sv_punkbuster in StartServer menu
+ + ta ui: added cl_punkbuster in server browser
+ + view filters are in a modal dialog
+ + new files: filter.menu menus.txt (pak3.pk3 updated)
+ + fix broken link in Linux FAQ
+
+2002-7-27 Timothee Besset <ttimo@idsoftware.com>
+ + ta ui: PB display in the browser, in it's additional tab, with sorting
+
+2002-7-26 Timothee Besset <ttimo@idsoftware.com>
+ + PB UI: for baseq3/ AND missionpack/
+ q3_ui: Punkbuster: Enable/Disable in server broswer (cl_punkbuster)
+ q3_ui: PB logo, PB Yes/No in browser (TODO: validate this to be working)
+ q3_ui: added sv_punkbuster toggle in start server menu
+ + automated building of the new PK3s, unix/Conscript-pk3
+
+2002-7-25 Timothee Besset <ttimo@idsoftware.com>
+ + added PB build scripts on Linux, fixed the Linux build
+
+2002-7-12 Timothee Besset <ttimo@idsoftware.com>
+ + https://zerowing.idsoftware.com/bugzilla/show_bug.cgi?id=511
+ fixing re.SetColor crash for widescreen displays (q3dm11)
+ was calling to the renderer while not registered
+
+2002-6-19 Timothee Besset <ttimo@idsoftware.com>
+ + r_roundImagesDown 0 + map q3dm16 -> tr_image.c ResampleTexture crash
+ buffer overflow because of resample to 2048x..
+ xian_q3dm12_leftwall4fin.jpg 1152x384
+ bumped one buffer byte p1[1024] -> byte p1[2048], added a safe check
+
+2002-6-14 Timothee Besset <ttimo@idsoftware.com>
+ + https://zerowing.idsoftware.com/bugzilla/show_bug.cgi?id=493
+ propagate a renderer fix from RTCW. fixes a one-frame visual glitch when mod code
+ registers a shader after drawsurfaces are generated but before the frame is rendered
+
+2002-6-12 Timothee Besset <ttimo@idsoftware.com>
+ + added cons and pcons to unix/, updated the build script
+
+2002-5-24 Timothee Besset <ttimo@idsoftware.com>
+ towards a new Q3 release?
+ some bug fixes require protocol change, or mod code/mod interface change to be fixed properly
+ this is a biz decision, dunno yet if we are going to want a new protocol (probably not)
+ -> have to create a branch for the 1.31b, i.e. backwards compatible with 1.31 'Stable-1_31'
+ and put the 1.32 specific / protocol changes on trunk
+ no telling what will go in SOS in the end .. probably 1.32
+
+2002-5-5 Timothee Besset <ttimo@idsoftware.com>
+ + https://zerowing.idsoftware.com/bugzilla/show_bug.cgi?id=491
+ adding a sv_lanForceRate (defaults to 1) to turn on/off server forcing rate of LAN clients
+ (only affects LAN dedicated clients - dedicated 1, default behaviour forces LAN clients to 99999 rate)
+ + https://zerowing.idsoftware.com/bugzilla/show_bug.cgi?id=470
+ fixing potential overflows with cl_cdkey (propagated from RTCW)
+ + cons-based build system (imported from Wolf, was partly written for mod tools release already)
+ building with SMP on by default
+ + better #ifdef SMP handling ('disabled at compile time' message)
+ + https://zerowing.idsoftware.com/bugzilla/show_bug.cgi?id=494
+ Q_vsnprintf for vsprintf calls in the core
+ not putting this in game code as we'd need a vsnprintf implementation in bg_lib.c
+
+2002-4-5 Timothee Besset <ttimo@idsoftware.com>
+ + https://zerowing.idsoftware.com/bugzilla/show_bug.cgi?id=462
+ taking out the fix which was found broken and incomplete
+
+2002-8-4 Timothee Besset <ttimo@idsoftware.com>
+ + adding NO_MOUSEGRAB define (select in the Makefile)
+
+2002-2-4 Timothee Besset <ttimo@idsoftware.com>
+ + applying Gareth's SMP patch
+ + count number of CPUs (Sys_ProcessorCount in unix_shared.c), default r_smp appropriately
+ + bumping version to 1.32
+ + if XInitThreads fails, set r_smp to zero
+
+2002-28-2 Timothee Besset <ttimo@idsoftware.com>
+ + https://zerowing.idsoftware.com/bugzilla/show_bug.cgi?id=462
+ send potential remaining fragmented packets before sending a gamestate
+
+2002-26-2 Timothee Besset <ttimo@idsoftware.com>
+ + https://zerowing.idsoftware.com/bugzilla/show_bug.cgi?id=455
+ removed old libMesaVoodooGL.so loading code
+ Voodoo cards should use XF4/DRI, that load code was outdated and confusing people with broken OpenGL
+
+2002-16-1 Timothee Besset <ttimo@idsoftware.com>
+ + https://zerowing.idsoftware.com/bugzilla/show_bug.cgi?id=441
+ adding brandelf calls to the setup building process so that our binaries run on BSD
+
+2002-1-1 Timothee Besset <ttimo@idsoftware.com>
+ + updated FAQ with BSD info (bug #441)
+ + FAQ update on CLIENT_UNKNOWN_TO_AUTH
+ + FAQ update for proper strace usage
+
+2001-12-12 Timothee Besset <ttimo@idsoftware.com>
+ + Q3 1.31 point release
+ updating build_setup.sh to new pk3 files
+ (baseq3/pak7.pk3 missionpack/pak2.pk3)
+ + https://zerowing.idsoftware.com/bugzilla/show_bug.cgi?id=395
+ adding quake3.xpm icon, and modified the setup accordingly to put symlinks
+ + https://zerowing.idsoftware.com/bugzilla/show_bug.cgi?id=390
+ ignoring SIGTTIN SIGTTOU
+
+2001-06-12 Timothee Besset <ttimo@idsoftware.com>
+ + https://zerowing.idsoftware.com/bugzilla/show_bug.cgi?id=402
+ bug with full scene
+
+2001-04-12 Timothee Besset <ttimo@idsoftware.com>
+ + https://zerowing.idsoftware.com/bugzilla/show_bug.cgi?id=398
+ cg_bobup cheat protect
+ + https://zerowing.idsoftware.com/bugzilla/show_bug.cgi?id=399
+ fixed Setup > System > Driver info crash
+ + checked in code/spank.sh script, perform checksuming
+
+2001-18-09 Timothee Besset <ttimo@idsoftware.com>
+ + https://zerowing.idsoftware.com/bugzilla/show_bug.cgi?id=371
+ propagating sound code fixes from Wolf to Q3
+
+2001-11-08 Timothee Besset <ttimo@idsoftware.com>
+ + setup script was still broken, damn shell expansion
+ the exit code for Q3 was always zero instead of $?
+ propagating the fix to Wolf
+
+2001-11-04 Timothee Besset <ttimo@idsoftware.com>
+ + https://zerowing.idsoftware.com/bugzilla/show_bug.cgi?id=382
+ modified challenge code for motd to be truly random
+
+2001-10-31 Timothee Besset <ttimo@idsoftware.com>
+ Moved updated q3asm and lcc source at the toplevel, MissionPack/q3asm
+ and MissionPack/lcc
+
+2001-10-29 Timothee Besset <ttimo@idsoftware.com>
+ + https://zerowing.idsoftware.com/bugzilla/show_bug.cgi?id=381
+ build system is now functional
+
+2001-10-21 Timothee Besset <ttimo@idsoftware.com>
+ + updated Sys_LoadDll code on linux to search in the following order:
+ #1 current directory
+ #2 fs_homepath
+ #3 fs_basepath
+ this was needed to make mod developement easier
+
+2001-10-09 Timothee Besset <ttimo@idsoftware.com>
+ + https://zerowing.idsoftware.com/bugzilla/show_bug.cgi?id=51
+ the code to buffer the redirection was in there but disabled? (Com_Printf)
+ enabled it back
+ + https://zerowing.idsoftware.com/bugzilla/show_bug.cgi?id=52
+ connection issues / userinfo
+ client side fix, instead of sending 'connect <userinfo>' packet
+ we now send 'connect "<userinfo>"'
+
+2001-10-08 Timothee Besset <ttimo@idsoftware.com>
+ + https://zerowing.idsoftware.com/bugzilla/show_bug.cgi?id=371
+ added a PROT_READ to the mmap call
+ this was needed to go around a bug in glibc i586 i686, memset doing read access
+ since the audio_fd is opened O_RDWD this is harmless to Q3
+
+2001-10-07 Timothee Besset <ttimo@idsoftware.com>
+ + updating from SOS
+ S_WriteLinearBlastStereo16 C/asm is back in snd_mix.c (Graeme)
+ r_showtris r_shownormal cheat protections
+ + Sys_LoadDll changes:
+ removing -debug search when loading native dlls
+ changing the fatal aborts when not finding native from release only to debug only (was a misfeature)
+ used to search in cd_path which is bogus, now searching in pwd if basepath fails
+ + https://zerowing.idsoftware.com/bugzilla/show_bug.cgi?id=275
+ fixed r_fullbright not being cheat protected / was a CVAR_LATCHED|CVAR_CHEAT issue
+
+2001-09-06 Timothee Besset <ttimo@idsoftware.com>
+ + updated from SOS, some changes to qcommon/unzip.c (statics)
+
+2001-08-27 Timothee Besset <ttimo@idsoftware.com>
+ + https://zerowing.idsoftware.com/bugzilla/show_bug.cgi?id=3
+ Added some code in CL_InitDownloads to use FS_ComparePaks and print out information about server-referenced paks that are missing on the client. It is a first step, allows to get precise information about what can cause a connection to fail (typically when the user is sent back to the main screen).
+
+2001-08-22 Timothee Besset <ttimo@idsoftware.com>
+ + https://zerowing.idsoftware.com/bugzilla/show_bug.cgi?id=86
+ fixed sound bug (with Graeme hints)
+
+2001-08-20 Timothee Besset <ttimo@idsoftware.com>
+ + made sure Sys_Printf doesn't get into an endless loop if logfile is on
+ fixed qconsole.log issues, +set logfile 1 +set fs_debug 1 was crashing (any OS)
+ fixed logfile 1 / ttycon 1 issue, didn't exit properly (same endless looping)
+ also fixes an issue reported by q3f team
+ + changed rcon commands from Com_DPrintf to Com_Printf so that they show up in the console
+ (with IP information)
+
+2001-08-19 Timothee Besset <ttimo@idsoftware.com>
+ + fixed https://zerowing.idsoftware.com/bugzilla/show_bug.cgi?id=91
+ (autodownload toggle in q3 ui)
+ + fixed https://zerowing.idsoftware.com/bugzilla/show_bug.cgi?id=76
+ g_password issue
+ + fixed https://zerowing.idsoftware.com/bugzilla/show_bug.cgi?id=93
+ cheat protecting r_lodCurveError
+ + wontfix https://zerowing.idsoftware.com/bugzilla/show_bug.cgi?id=92
+
+2001-08-18 Timothee Besset <ttimo@idsoftware.com>
+ + more fixes to the 7-button mouse code (linux only)
+ + updated faq about gamma slider
+ + added "servers don't show up in ingame browser" to faq
+ + added Alt+Enter toggle for fullscreen/windowed (linux)
+
+2001-08-16 Timothee Besset <ttimo@idsoftware.com>
+ reconfiguring CVS repository to give access to Gareth
+ + testin gareth's access
+
+2001-08-03 Timothee Besset <ttimo@idsoftware.com>
+ * https://zerowing.idsoftware.com/bugzilla/show_bug.cgi?id=85
+ fixes in the setup code for older bash versions
+
+2001-08-02 Timothee Besset <ttimo@idsoftware.com>
+ * commented out assembly implementation of S_WriteLinearBlastStereo16, using modified C implementation from Zaphod
+ need to check performance: https://zerowing.idsoftware.com/bugzilla/show_bug.cgi?id=88
+ * finished const declarations in CG_Trace calls, was needed in pmove_t declaration and some other functions
+ cgame/cg_local.h : CG_trace trap_CM_BoxTrace
+ game/bg_public.h : using const in pmove_t trace functions prototypes
+ (fixes gcc warnings: assignment from incompatible pointer type)
+
+2001-07-26 Timothee Besset <ttimo@idsoftware.com>
+ * https://zerowing.idsoftware.com/bugzilla/show_bug.cgi?id=78
+ mapped K_MOUSE4 K_MOUSE5
+
+2001-07-23 Timothee Besset <ttimo@idsoftware.com>
+ * https://zerowing.idsoftware.com/bugzilla/show_bug.cgi?id=5
+ more fixes, handling meta characters and various kinds of backspace
+
+2001-07-22 Timothee Besset <ttimo@idsoftware.com>
+ * https://zerowing.idsoftware.com/bugzilla/show_bug.cgi?id=5
+ after testing feedback, fixed more stuff:
+ better backspace, works with putty and potentially more terminals
+ * https://zerowing.idsoftware.com/bugzilla/show_bug.cgi?id=51
+ band aid fix to rcon status, incresed MAX_PUSHED_EVENTS from 256 to 1024
+ (adds 28kb of mem requirements)
+
+2001-07-21 Timothee Besset <ttimo@idsoftware.com>
+ * https://zerowing.idsoftware.com/bugzilla/show_bug.cgi?id=2
+ using XF86 Gamma extension to set the gamma in game from the menus
+ (previous behaviour was to set /r_gamma and restart, renderer relying on s_gammatable)
+ restoring initial gamma on GLimp_ShutDown
+
+2001-07-19 Timothee Besset <ttimo@idsoftware.com>
+ * https://zerowing.idsoftware.com/bugzilla/show_bug.cgi?id=5
+ first usable version of dedicated console
+ added history and completion functionality
+ ready for some testing
+ still some TODOs and FIXMEs:
+ keep the currently edited line when going back from history exploration
+ edit the current line with cursor, insert mode etc.
+
+2001-07-18 Timothee Besset <ttimo@idsoftware.com>
+ * starting TAB completion and history for the dedicated server (tty console)
+ removed Sys_ConsoleOutput (unused)
+ removing bogus nostdout variable
+ cleanup of a big chunk of code that Bernd commented out and scheduled for deletion
+ moved completion code from client/cl_keys.c stuff into qcommon/common.c, Field_CompleteCommand(field_t*)
+
+2001-07-13 Timothee Besset <ttimo@idsoftware.com>
+ * fixed https://zerowing.idsoftware.com/bugzilla/show_bug.cgi?id=8
+ screenshots overwrites
+ * fixed https://zerowing.idsoftware.com/bugzilla/show_bug.cgi?id=31
+ DOUBLE SIGNAL FAULT
+
+2001-07-11 Timothee Besset <ttimo@idsoftware.com>
+ * fix for french keybards / console toggle / bound to XK_twosuperior
+
+2001-07-10 Timothee Besset <ttimo@idsoftware.com>
+ * https://zerowing.idsoftware.com/bugzilla/show_bug.cgi?id=19
+ cleanup of the keyboard code, adding com_developer message in case XLookupString would fail
+
+2001-07-10 Timothee Besset <ttimo@idsoftware.com>
+ * https://zerowing.idsoftware.com/bugzilla/show_bug.cgi?id=33
+ using our custom handlers for X errors, should make things more robust
+ (X docs say some X errors are not fatal, but the default X handler exits the app anyway)
+
+2001-07-08 Timothee Besset <ttimo@idsoftware.com>
+ * https://zerowing.idsoftware.com/bugzilla/show_bug.cgi?id=19
+ keyboard state issues, fixed the sticking with ctrl key (thks relnev)
+
+2001-07-07 Timothee Besset <ttimo@idsoftware.com>
+ * closing https://zerowing.idsoftware.com/bugzilla/show_bug.cgi?id=13
+ the fixes to bug #9 solved this one too
+ * checking in to SOS
+
+2001-07-05 Timothee Besset <ttimo@idsoftware.com>
+ * work on https://zerowing.idsoftware.com/bugzilla/show_bug.cgi?id=9
+ filesystem code changes:
+ updated the documentation in files.c to the current system
+ added correct fs_homepath fs_basepath fs_cdpath scanning to FS_SV_FOpenFileRead
+ (fixes description.txt not found, and probably a few other linux issues)
+
+2001-06-29 Timothee Besset <ttimo@idsoftware.com>
+ * fixed setup issues (graphical/console)
+ https://zerowing.idsoftware.com/bugzilla/show_bug.cgi?id=6
+
+2001-06-26 Timothee Besset <ttimo@idsoftware.com>
+ * bug tracker is online at https://zerowing.idsoftware.com/bugzilla
+ authentication, use login: bugs password: b00gies
+ for now, using it as the linux bug tracker, possible use for more OSes and programs if anyone is interested.
+ * tweaked the graphical setup to send to bugs@idsoftware.com on errors instead of support@lokigames.com
+
+2001-06-19 Timothee Besset <ttimo@idsoftware.com>
+ * fixed generated launch script /usr/local/bin/quake3, exit $* should be exit
+
+2001-06-18 Timothee Besset <ttimo@idsoftware.com>
+ * rebuilt 1.29f setups, released as 1.29f-beta1 'Q3 1.29f linux-i386 Jun 19 2001'
+
+2001-06-10 Timothee Besset <ttimo@idsoftware.com>
+ * rebuilt against PR source, packaged 1.29b setups
+
+2001-05-25 Timothee Besset <ttimo@idsoftware.com>
+ * graphical setup, based on Loki's setup tool (GPL)
+
+2001-05-22 Timothee Besset <ttimo@idsoftware.com>
+ * changed fs_basepath to fs_homepath, according to Graeme's changes (probably missed this change?)
+ this fixes the q3key prompting at each game startup
+
+2001-05-20 Timothee Besset <ttimo@idsoftware.com>
+ * rebuilding 1.28b, various fixes on linux build:
+ - SetProgramPath was renamed to Sys_SetDefaultCDPath in unix_shared.c
+ updated unix_main.c accordingly
+ - some prototypes in qgl.h are guarded by #ifndef GL_VERSION_1_2 (ARB extentions)
+ those prototypes are needed by linux_glimp for importing functions and casting, added a #ifdef __linux__
+ (not a clean solution)
+ - game/q_shared.h
+ little endian / big endian functions have been added
+ gcc generates warnings about functions being unused .. inlined them
+ - cgame/cg_marks.c
+ // TTimo
+ // gcc warning: might be used uninitialized
+ float sInc = 0.0;
+ float s = 0.0;
+
+2001-05-15 Timothee Besset <ttimo@idsoftware.com>
+ * fixes to linux Makefile for bspc 2.1h
+ * various updates to 1.28b on linux
+
+2001-05-09 Timothee Besset <ttimo@idsoftware.com>
+
+ * R. Duffy reverted game/bg_pmove.c PM_CheckDuck, was a merging screup on my side
+ * updated setup to 1.27z, removed the .so from the setup distribution (they were in 1.27g because of issues)
+ FIXME: gotta get pk3's first
+
+2001-05-04 Timothee Besset <ttimo@idsoftware.com>
+
+ * fixes to gcc, building RC for 1.27s
+
+2001-05-01 Timothee Besset <ttimo@idsoftware.com>
+
+ * added qcommon/huffman.c to the Makefile
+ * gcc -Wall:
+ commenting out
+ CL_Netchan_Encode CL_Netchan_Decode (cl_net_chan.c)
+ Netchan_ScramblePacket Netchan_UnScramblePacket (net_chan.c)
+ SV_Netchan_Encode SV_Netchan_Decode (sv_net_chan.c)
+
+2001-04-26 Timothee Besset <ttimo@idsoftware.com>
+
+ * fixed dedicated server crash when entering the VM_COMPILED qagame on a mod (some statics lacked initialization)
+
+2001-04-25 Timothee Besset <ttimo@idsoftware.com>
+
+ * added $(Q3POBJ) to clean target (cleanup of platform-dependent objects)
+ * more make clean improvements
+
+2001-04-23 Timothee Besset <ttimo@idsoftware.com>
+
+ * cleanup the mod selection code, remove duplicates
+ * some issues with release builds, my main developement box doesn't build stable binaries with release settings
+ removing -fomit-frame-pointer seems to fix (there's probably a performance hit)
+ see OMIT-FRAME-POINTER.txt
+
+2001-04-13 Timothee Besset <ttimo@idsoftware.com>
+
+ * checked in a first set of merged files
+
+2001-04-06 Timothee Besset <ttimo@idsoftware.com>
+
+ * merged back the core linux parts to make 1.27g linux build from the Source Safe tree again
+
+2001-02-27 Bernd Kreimeier <bk@lokigames.com>
+
+ * CVS: tag with changes as of today
+ cvs tag id1-27j-loki01027
+
+ * code/qcommon/msg.c: numFields loop (SOS).
+ * code/qcommon/files.c: ue Q_stricmp (SOS uses stricmp, was strcmp).
+ * code/game/q_shared.h (Q3_VERSION): 1.27j. Also
+ MAX_STRING_TOKENS upped from 256 to 1024 (SOS).
+
+ * code/server/sv_snapshot.c (SV_AddEntitiesVisibleFromPoint): see below.
+ * code/game/g_public.h (SVF_NOTSINGLECLIENT): added (SOS).
+
+ * code/server/sv_ccmds.c: see below.
+ * code/game/g_main.c: g_gametype cvar now userinfo (SOS).
+
+ * code/game/g_active.c (SendPendingPredictableEvents): new (SOS).
+ * code/game/bg_misc.c: new SOS (sos010227)
+
+ * SOS: new update sos010227.
+
+2001-02-22 Bernd Kreimeier <bk@lokigames.com>
+
+ * CVS: now in sync with last SOS and cleanup up
+ cvs tag id1-27i-loki01022
+
+ * code/ui/ui_shared.c: below.
+ * code/ui/ui_main.c: leftover code!
+ * code/server/sv_world.c: below.
+ * code/server/sv_snapshot.c: below.
+ * code/server/sv_init.c: below.
+ * code/server/sv_game.c: below.
+ * code/server/sv_client.c: below.
+ * code/server/sv_ccmds.c: below.
+ * code/server/sv_bot.c: below.
+ * code/server/server.h: below.
+ * code/renderer/tr_surface.c: below.
+ * code/renderer/tr_shader.c: changed assert to early return.
+ * code/renderer/tr_shade_calc.c: below.
+ * code/renderer/tr_shade.c: below.
+ * code/renderer/tr_scene.c: below.
+ * code/renderer/tr_mesh.c: below.
+ * code/renderer/tr_local.h: below.
+ * code/qcommon/vm_x86.c: cleanup.
+ * code/qcommon/vm.c: below.
+ * code/qcommon/unzip.c: below.
+ * code/qcommon/qcommon.h: below.
+ * code/qcommon/files.c: below.
+ * code/qcommon/cvar.c: cleanup.
+
+2001-02-21 Bernd Kreimeier <bk@lokigames.com>
+
+ * code/qcommon/common.c: cleanup.
+ * code/qcommon/cm_trace.c: cleanup.
+ * code/qcommon/cm_patch.c: cleanup.
+ * code/qcommon/cm_public.h: cleanup.
+ * code/game/q_shared.h: cleanup.
+ * code/game/q_shared.c: cleanup.
+ * code/game/q_math.c: cleanup.
+ * code/game/g_syscalls.asm: changed (once more) floor,ceil etc.
+ * code/game/g_spawn.c: cleanup.
+ * code/game/g_session.c: cleanup.
+ * code/game/g_cmds.c: cleanup.
+ * code/game/g_client.c: cleanup.
+ * code/game/g_arenas.c: cleanup.
+ * code/game/bg_slidemove.c: cleanup.
+ * code/game/bg_pmove.c (PM_CheckDuck): old call to trace?
+ * code/game/bg_misc.c: cleanup.
+ * code/game/be_aas.h: dead code.
+ * code/game/ai_dmq3.c: cleanup. One clear/copy switched?
+ * code/game/ai_dmnet.c: more //*/. Why oh why not DEBUG....
+
+ * code/client/snd_mix.c: below.
+ * code/client/snd_dma.c: below.
+ * code/client/keys.h: cleanup.
+ TODO: #error in q3_ui/keycodes.h ?
+
+ * code/client/client.h: cleanup.
+ * code/client/cl_main.c: misplaced bracket. Cleanup.
+ * code/client/cl_keys.c: below.
+ * code/client/cl_cin.c: below.
+ * code/client/cl_cgame.c: cleanup.
+ TODO: define assert for Win32 or guard my assertions.
+
+ * code/cgame/cg_syscalls.c: below.
+ * code/cgame/cg_servercmds.c: below.
+ * code/cgame/cg_players.c: cleanup.
+
+ * code/cgame/cg_newdraw.c: remember to diff against cg_newDraw.c
+ in SOS (mixed case).
+ TODO: get id to use cg_newdraw.c, and to remove cg_newDraw.c/cpp.
+
+ * code/cgame/cg_main.c: below.
+ * code/cgame/cg_local.h: below.
+ * code/cgame/cg_event.c: below.
+ * code/cgame/cg_drawtools.c: below.
+ * code/cgame/cg_draw.c: cleanup.
+ * code/cgame/cg_consolecmds.c: dead code.
+ * code/bspc/qbsp.h: below.
+ * code/bspc/l_poly.c: below.
+ * code/bspc/l_math.c: cleanup.
+ * code/bspc/bspc.c: cleanup.
+ * code/bspc/be_aas_bspc.c: cleanup.
+ * code/bspc/aas_map.c: kept comments - merge loss at their end?
+ * code/bspc/aas_file.c: cleanup.
+
+ * code/botlib/be_interface.c: this file is plain impossible. There
+ are layers of code made dead with /* */ and the resurrected by
+ //* or // /* or variations of this. I reverted to exact mirror
+ image of SOS to be sure - short of removing it's too easy to mistake
+ live code for dead one.
+ Later: have to change 5 occurences to avoid gcc complaints about
+ nested comment tokens.
+ TODO: somebody please get rid of the cruft in here.
+
+ * code/botlib/be_ai_move.c: redundant typedef.
+ * code/botlib/be_ai_chat.c: assertions on signed string index.
+ Note: this is not in my ChangeLog - ouch.
+ TODO: use gcc -fsigned-char on all platsforms to enforce Win32
+ TODO behavior (PPC has a default unsigned char, Intel has not).
+ * code/botlib/be_aas_sample.c (AAS_TraceClientBBox): one code block
+ was placed in different location, and one FPE hack not used. I would
+ expect that divide by zero will still occur here.
+
+ * code/botlib/be_aas_reach.c: below.
+ * code/botlib/be_aas_cluster.c: cleanup.
+ * CVS: the last tag (below) marks the version with a lot of history
+ and additional comments. I am now bringing the codebase in sync with
+ SOS as of yesterday, cleaning out comments, dead code and other
+ differences to minimize a diff - in a valiant if futile attempt to
+ roll back changes into the id codebase.
+ Note: I ignore the $SOS$ - these are unfortunate but will change
+ in the same awkward way at their end.
+ Note: I stick to #if 0 instead of C comments around dead code id
+ kept (nested comments issue). The commentary is changed to sosYYMMDD
+ and includes the token DEAD.
+
+2001-02-20 Bernd Kreimeier <bk@lokigames.com>
+
+ * CVS: update, then tag current version as
+ cvs tag id1-27i-loki010219
+
+ * SOS: patched up to sos010219.
+
+ * code/qcommon/cm_trace.c (CM_Trace): fabs on sphere offsets (SOS).
+ * code/game/bg_slidemove.c (PM_StepSlideMove): stepSize vs. STEPSIZE (SOS).
+ * code/game/bg_pmove.c (PM_CheckDuck): fix in stand up check (SOS).
+ * code/bspc/bspc.c (main): -capsule (SOS).
+ * code/bspc/qbsp.h: below (SOS).
+ * code/bspc/be_aas_bspc.c (capsule_collision): added (SOS).
+ * code/bspc/aas_map.c (CapsuleOriginDistanceFromPlane): added and used (SOS).
+ * code/bspc/aas_file.c (AAS_WriteAASFile): removed diagnostics recently
+ added. No matter how long you wait, they'll always get you ;-).
+ * code/botlib/be_aas_cluster.c: enabled LogWrites, different flood (SOS).
+
+ * SOS: patching up to snapshot sos010219.
+ Note: For brevity, I use as marker sosYYMMDD now instead of bkYYMMDD, to
+ distinguish from changes not in SOS.
+
+ * CVS: tagged current version before patching up with SOS.
+ cvs tag id1-27i-loki010216-bsd
+
+2001-02-16 Bernd Kreimeier <bk@lokigames.com>
+
+ * code/server/sv_init.c: DLL_ONLY sets sv_pure to 0 and ROM.
+ TODO: determine good sv_pure policy for DLL-only servers.
+
+ * code/renderer/tr_shade_calc.c: my_ftol implementation (BSD).
+
+ * code/unix/Makefile: FreeBSD sections.
+ TODO: include target-specific Make-freebsd etc.,
+ include a Make-local not in CVS for build preferences,
+ and generally clean up this mess.
+ * code/unix/unix_glw.h: guard #error
+ * code/unix/linux_snd.c: soundcard.h location (BSD).
+ * code/unix/linux_glimp.c: guard system headers.
+ Later: added Joystick stubs.
+ Note: linux_ etc. prefixes start to loose meaning as we
+ re-use most of this on UNIXes anyway. I didn't use Raf's
+ freebsd_joystick.c but instead put generic stubs here.
+ TODO: introduce generic -DNO_JOYSTICK flag.
+ * code/renderer/tr_local.h: my_ftol guard.
+ * code/renderer/qgl.h: FreeBSD guards.
+ * code/qcommon/vm_x86.c: sys/types include on FreeBSD.
+ * code/qcommon/md4.c: Win32 pragma guard.
+ * code/qcommon/common.c: Com_Memcpy/Memset external.
+ * code/game/q_shared.h: added FreeBSD defines.
+ * code/game/q_math.c (BoxOnPlaneSide): FreeBSD conditional.
+ TODO: check whether we have/need the assembly version anyway.
+ * code/client/snd_mix.c: use C fallback on FreeBSD.
+ Note: all of the above changes from the original port by Rafael Barrero.
+
+ * CVS: tagged current version before merging FreeBSD related changes.
+ cvs tag id1-27i-loki010215-ppc
+
+2001-02-15 Bernd Kreimeier <bk@lokigames.com>
+
+ * code/unix/Makefile: BSD related changes.
+ * code/cgame/cg_draw.c: hacked phone jack rendering check for Debug.
+ TODO: finish Debug, fix CG_DrawDisconnect !!!
+
+ * code/unix/vm_x86.c: error on compile attempts. Fight redundancy!
+ * code/qcommon/vm_x86.c (VM_CallCompiled): dummy for linkage on PPC.
+ Note: DLL_ONLY is the global Makefile option for DLL-only builts.
+ Currently only executed on Linux.
+ * code/unix/unix_main.c: *ppc postfix for DLLs. Ignored the changes
+ to redundant code (have to remove the unused Un/LoadDll/API calls).
+ * code/server/sv_game.c (VMA): changed macro (see below). PPC.
+ * code/qcommon/vm.c (VM_DllSyscall): see lengthy commentary by Ryan.
+ The existing VM code makes certain assumptions about the layout of
+ varargs on the stack, which fall apart with call conventions that
+ don't even put all parameters on the stack (gcc on PPC, register-rich).
+ Using a dedicated memory area as our own stack. This should actually
+ be the default behavior.
+ Later: make vm_* cvars INIT/ROM for DLL_ONLY target.
+
+ * code/qcommon/common.c: PPC change (from Ryan Gordon).
+
+2001-02-07 Bernd Kreimeier <bk@lokigames.com>
+
+ * code/unix/unix_main.c: disabled FPE for debug for the time
+ being (that is, until I can figure out
+ Program received signal SIGFPE, Arithmetic exception.
+ RB_BeginSurface (shader=0x449572e0, fogNum=0) at ..//renderer/tr_shade.c:307
+ 307 tess.shaderTime = backEnd.refdef.floatTime - tess.shader->timeOffset;
+ without any NaN's involved.
+ TODO: unmask other FPE's selectively (see Mike's Tribes2, no getenv though).
+
+2001-02-06 Bernd Kreimeier <bk@lokigames.com>
+
+ * SOS: up to date with todays snapshot.
+ Note: got the date wrong, comment used was bk010205. Duh.
+
+ * code/server/sv_snapshot.c (SV_UpdateServerCommandsToClient): below.
+ * code/server/sv_main.c (SV_ReplacePendingServerCommands): new (SOS).
+ * code/server/server.h: reliableSent (SOS).
+
+ * code/renderer/tr_shade.c (ProjectDlightTexture): see below.
+ * code/renderer/tr_scene.c: see below.
+ * code/renderer/tr_public.h: see below (SOS).
+ * code/renderer/tr_local.h: additive light support (SOS).
+
+ * code/qcommon/cm_trace.c (CM_Trace): new tw.sphere.use branch (SOS).
+
+ * code/game/g_spawn.c: notta, notq3a entities (SOS).
+ * code/game/ai_dmq3.c: MAX_ACTIVATEAREAS search (SOS).
+
+ * code/client/cl_cgame.c: see below.
+ * code/cgame/cg_syscalls.c (trap_R_AddAdditiveLightToScene): below.
+ * code/cgame/cg_syscalls.asm: see below (trap_R_AddAdditiveLightToScene).
+ * code/cgame/cg_public.h: CG_R_ADDADDITIVELIGHTTOSCENE (SOS).
+
+ * code/bspc/l_math.c: new VectorLengthSquared, removed rotate/matrix (SOS).
+ * code/bspc/bspc.c (BSPC_VERSION): was 2.1e, now?
+ * code/bspc/be_aas_bspc.c (BotImport_Trace): CM_BoxTrace sig. (SOS).
+ * code/bspc/aas_file.c (AAS_WriteAASFile): SOS.
+ * code/botlib/be_aas_sample.c (AAS_DeAllocAASLink): SOS.
+
+ * code/unix/unix_main.c (Sys_LoadDll): do not load from installdir
+ in NDEBUG (confusing relic from old Makefile). Postfix -debug.so
+ for debug binaries to let both builds coexist.
+
+ * code/unix/Makefile: updated install targets and VERSION.
+
+ * Win32: build from SOS snapshot.
+ Note: Unix CR/LF in *.dsw/*.dsp fucks up MSVC++.
+
+2001-02-02 Bernd Kreimeier <bk@lokigames.com>
+
+ * SOS: all changes up to today.
+
+ * code/server/sv_init.c (SV_TouchCGame): added. Also memset
+ on reallocated client data (SOS).
+ * code/qcommon/qcommon.h: see below.
+ * code/qcommon/cvar.c (Cvar_SetLatched): new (SOS).
+ * code/qcommon/cm_trace.c: more sphere test fixes (SOS).
+ Note: SOS encryption key expired and updated by MrElusive.
+
+ * code/qcommon/cm_patch.c (CM_TraceThroughPatchCollide):
+ fix from MrElusive, fall through curved corner floors (q3dm17).
+ Later: also in SOS (so is shadow FPE fix).
+
+ * Win32: can't get an unadulterated SOS snapshot to build.
+ First, fix CR/LF back again (Linux client converts all).
+ find . -name '*.ds*' -print
+ alias dos2unix='recode ibmpc..lat1'
+ alias unix2dos='recode lat1..ibmpc'
+ Next, find a *.dws that works? Nope, no cigar.
+
+2001-02-01 Bernd Kreimeier <bk@lokigames.com>
+
+ * Win32: have to update dsp/dsw/etc. files in CVS, too.
+
+ * CVS: tag previous version before update
+ cvs tag id1-27h-loki010131-beta3
+
+ * code/game/q_math.c (Q_rsqrt): guard, #ifndef __linux__
+ for assert (for Win32 build).
+ TODO: assert replacement for Win32?
+ * code/q3_ui/ui_qmenu.c: see below.
+ * code/q3_ui/ui_players.c: see below.
+ * code/q3_ui/ui_controls2.c: float const with f postfix
+ Note: Win32 C4305 warning. Somebody at id has been doing
+ a lot of these recently as well...
+
+ * code/cgame/cg_players.c (CG_PlayerShadow): applied fix by
+ MrElusive, removed FPE hack (player shadows on zero mormals).
+ Prolly in this evenings' CVS.
+
+ * code/server/sv_game.c: new signatures (capsule again).
+ * code/server/server.h: new signatures (SV_Trace,ClipToEntity).
+ * code/server/sv_bot.c: new signatures (above).
+ * code/qcommon/cm_trace.c: a truckload of changes. Math
+ code added before moved upwards. Capsule traces added all
+ over the place, old box traces moved in conditional
+ branches, functions renamed and wrapped. Eliminated some
+ of the previous' versions deadcode to keep diffs smaller.
+ TODO: once a point release is out and reasonably bug
+ TODO free, remove // bkYYMMDD annotations where SOS related.
+
+ * code/qcommon/cm_public.h: new signatures in prototypes.
+ * code/qcommon/cm_patch.c: dead code re-enabled, new
+ sections (conditional branches for spheres) added to
+ several trace functions.
+ * code/qcommon/cm_local.h (CAPSULE_MODEL_HANDLE): added.
+ * code/qcommon/cm_load.c (CM_TempBoxModel): capsules.
+
+ * code/game/q_shared.h (Q3_VERSION): 1.27i now (new QVM traps).
+
+ * code/game/g_syscalls.asm: see below.
+ * code/game/g_public.h (SVF_CAPSULE): added (SOS). Also
+ G_ entry poiints for capsule traces.
+
+ * code/client/cl_cgame.c: see below.
+ * code/cgame/cg_syscalls.c: see below.
+ * code/cgame/cg_syscalls.asm: see below.
+ * code/cgame/cg_public.h: new capsule trace code (SOS).
+
+2001-01-31 Bernd Kreimeier <bk@lokigames.com>
+
+ * Win32: test compile (WinCVS, MSVC++). Have to guard isnan.
+ Note: too much shit going on....
+
+2001-01-30 Bernd Kreimeier <bk@lokigames.com>
+
+ * CVS: update for patching up (pre-1.27i).
+
+ * SOS: new changes (new collision detection primitives).
+ Now Version 1.27i.
+ TODO: start testing using DLL's (QVM code is out of sync).
+
+2001-01-25 Bernd Kreimeier <bk@lokigames.com>
+
+ * SOS: caught up till today (below).
+ * code/qcommon/cm_trace.c: new functions added: RotatePoint,
+ TransposeMatrix, CreateRotationMatrix (SOS).
+ (CM_TransformedBoxTrace): new rotation code used here.
+
+ * code/q3_ui/ui_demo2.c: sizeof(extension). SOS.
+ * code/game/g_cmds.c (G_SayTo): CON_CONNECTED.
+ * code/game/ai_main.c: HOOK added (SOS).
+ * code/botlib/be_aas_move.c (AAS_HorizontalVelocityForJump):
+ correct fix for FPE occuring (SOS).
+ * code/game/ai_dmq3.c: initmove.viewoffset (SOS).
+
+ * code/game/q_math.c: guard asser/isnan with Q3_VM (q3asm).
+ TODO: define Com_Error based assert macro? NDEBUG?
+
+2001-01-24 Bernd Kreimeier <bk@lokigames.com>
+
+ * code/server/sv_ccmds.c (SV_MapRestart_f): some debug.
+ TODO: map_restart 0 disconnects external client in 1.27h?
+
+ * code/renderer/tr_image.c (LoadTGA): added some commentary
+ and dead code based on fixes from GtkRadiant (Leonardo found
+ flipped TGA's).
+
+2001-01-23 Bernd Kreimeier <bk@lokigames.com>
+
+ * BETA3: finished testing, ready to upload to id FTP.
+ Later: neither the FreeBSD beta not the Linux Beta3
+ uloaded. Beta2 not yet released, and clients get
+ disconnected with Beta2 and Beta3 on SV_MapRestart_f.
+
+2001-01-22 Bernd Kreimeier <bk@lokigames.com>
+
+ * code/client/cl_main.c (CL_InitDownloads): undid yesterday (SOS).
+ * code/botlib/be_aas_sample.c (AAS_DeAllocAASLink): guard print (SOS).
+ * code/server/sv_client.c (SV_DirectConnect): VM_Call disconnect (SOS).
+ * code/qcommon/files.c (FS_ListFilteredFiles): trailing slashes (SOS).
+ * code/game/g_cmds.c (SetTeam): print change (SOS).
+ Note: the above plus VectorClear(v1) (below) are todays SOS changes.
+
+ * code/cgame/cg_players.c (CG_PlayerShadow): ignore bogus
+ (all zero) planes. This caused FPE in ProjectPointOnPlane.
+ TODO: why does trace return zero normal planes?
+ Note: gdb seems totally at loss with vec3_t arrays....
+
+ * code/botlib/be_aas_sample.c (AAS_TraceAreas): FPE.
+ NaN in uninitialized v1 that wasn't supposed to be referred
+ to in this branch.
+
+ * code/botlib/be_aas_move.c (AAS_HorizontalVelocityForJump):
+ FPE divide by zero (zero zvel, zero t) for jump estimates.
+
+ * code/client/cl_main.c (CL_Frame):1856. uivm==NULL on
+ client after server crashed.
+ TODO: check that uivm always non-NULL for client.
+ TODO: do setenv(FX_NO_SIGNALS) to avoid exit errors...
+
+ * code/unix/linux_glimp.c (GLW_SetMode): added "Indirect"
+ Mesa token to software rendering detection. Reworded error
+ output and added drivername.
+ TODO: measure framerate instead?
+
+2001-01-21 Bernd Kreimeier <bk@lokigames.com>
+
+ * SOS: caught up with changes up until today.
+
+ * code/server/sv_init.c (SV_SetConfigstring): gentity != NULL
+
+ * code/server/sv_client.c: connect to "{all bots" server.
+ * code/renderer/tr_init.c: JPEG extension on screenshots
+ * code/qcommon/files.c: modes based on mods, fs_basegame
+
+ * code/q3_ui/ui_demo2.c: dm3 extension (demo names, protocol).
+
+ * code/game/g_client.c: savedEvents[] removed.
+ * code/game/bg_misc.c: event sequence fixes.
+ * code/client/snd_dma.c (S_StopBackgroundTrack): different use.
+ * code/client/cl_main.c: demo file handling changed (names).
+ Also CL_InitDownloads: always next download.
+
+ * code/cgame/cg_servercmds.c: cg_thirdPerson.
+ * code/cgame/cg_weapons.c: see below.
+ Also CG_ShotgunPattern: different call (seed parameter).
+
+ * code/cgame/cg_main.c: see below.
+ * code/cgame/cg_local.h: new cg_noProjectileTrail Cvar.
+ * code/cgame/cg_effects.c (CG_BubbleTrail): early out (above).
+
+ * code/bspc/l_poly.c (BOGUS_RANGE): increased.
+ * code/bspc/bspc.c: applied patch up to "2.1e"
+
+2001-01-18 Bernd Kreimeier <bk@lokigames.com>
+
+ * code/ui/ui_main.c: below.
+ * code/q3_ui/ui_main.c: UI_HASUNIQUECDKEY comment.
+ Note: mods have to return qfalse. See Bug #2890 in Fenris.
+
+2001-01-17 Bernd Kreimeier <bk@lokigames.com>
+
+ * BETA2: finished testing, uploaded to id's FTP for release.
+
+2001-01-16 Bernd Kreimeier <bk@lokigames.com>
+
+ * CVS: checking in preparation for Beta2.
+ cvs tag id1-27h-loki010116-beta2
+
+ * SOS: new bspc "2.1e". No change on 1.27h.
+
+ * TEST: patch-up seems to work fine. No new files have been added
+ to the linkage (i.e. the ft2/ files now added), so we might not be
+ feature complete.
+
+ * code/game/g_active.c (ClientThink_real): id MISSIONPACK
+ conditional in addition to the ones I added earlier.
+ * code/qcommon/files.c: REJECT. Linux hack for userdir threw it off.
+ * code/qcommon/unzip.c: REJECT. CRC-32 section removed.
+ Later: unused tempB
+
+ * code/q3_ui/ui_syscalls.asm: REJECT. Start/StopBackgroundTrack.
+ * code/ui/ui_syscalls.asm: REJECT. syscalls ids from 1.27h
+ as of SOS (floor/ceil - will this ever get straightened out)
+ * code/win32/win_input.c: REJECT. g_pMouse edit.
+ * ui/menus.txt: REJECT. Replaced with 1.27h version.
+ Note: some more due to $SOS$.
+
+ * ui/: new scripts.
+ cinematicmenu.menu, demo_quit.menu, ingame.txt, serverinfo_old.menu
+ vid_restart.menu
+
+ * code/ft2/ttconfig.h: below.
+ * code/ft2/sfconfig.h: below.
+ * code/ft2/pstables.h: below.
+ * code/ft2/psnames.c: below.
+ * code/ft2/psdriver.h/c: below.
+ * code/ft2/keys.h: below.
+ * code/ft2/ftbbox.c: new in 1.27h
+
+ * code/cgame/cg_newdraw.c: beware: cg_newDraw.c gets lost in diff easily.
+
+ * code/cgame/cg_rankings.c: file removed from SOS.
+
+2001-01-15 Bernd Kreimeier <bk@lokigames.com>
+
+ * Patch-up: patching up from RC4 to 1.27h current.
+ No changes since 010112 snapshot.
+ ln -s sos010112/ work
+ diff -urbB sos001204-rc4/ work > work.diff
+ ln -s cvs-1.27g/ work
+ patch -p0 < work.diff > work.patch
+ find cvs1.27g/ -name '*.rej' -print
+
+ * SOS: adding the remaining SOS snapshots to CVS.
+ cvs import Quake3_sos sos001211 pr1-27g-win32-001211
+ Note: at this point id warned about repository corruption.
+ Watch out for the syscall stuff in particular.
+ cvs import Quake3_sos sos010104 pr1-27g-win32-010104
+ cvs import Quake3_sos sos010108 pr1-27h-win32-010108
+ cvs import Quake3_sos sos010110 pr1-27h-win32-010110
+ cvs import Quake3_sos sos010112 pr1-27h-win32-010112
+ Note: the first 1.27h might be the public (server only)
+ beta released, the second one was post release. Beware
+ of source files added and removed (botlib headers, FT2).
+ Note: why so late? Don't ask...
+
+2001-01-08 Bernd Kreimeier <bk@lokigames.com>
+
+ * SOS: id's working up to 1.27h (server side fix for
+ Guard exploit seems to force earlier release). Updating
+ CVS (most of the changes are debug code put in and
+ then disabled, plus some fixes as below). Next patching
+ up to current SOS.
+
+2001-01-07 Bernd Kreimeier <bk@lokigames.com>
+
+ * Makefile: need to rework this for multiple platforms.
+ We also need null/null_vm.c for platforms where we don't
+ have JIT (assembly emit).
+
+2001-01-04 Bernd Kreimeier <bk@lokigames.com>
+
+ * code/q3_ui/ui_connect.c (UI_DisplayDownloadInfo): time
+ information for current (vs. start of download) is wrong,
+ thus negative 1 "estimated time", as well as transfer
+ rate just negative downloadSize. Not fixed.
+
+ * code/unix/unix_main.c (Sys_ParseArgs): added.
+ Note: for support/us, to identify builts. This is only
+ a skeleton right now - if I ever feel the need to support
+ more than "-v" and "--version" I'll have to flesh this out.
+
+ * code/unix/linux_glimp.c (signal_handler): see below.
+ * code/unix/unix_main.c (Sys_Exit): added an abstraction
+ layer for exit/_exit/assert/raise issues.
+ Note: need both a better debug/backtrace handling, and
+ have to find a way to determine why/where the alleged
+ startup/exit errors happen...
+
+2001-01-03 Bernd Kreimeier <bk@lokigames.com>
+
+ * code/game/g_mem.c (G_Alloc): ERR_DROP initiated by
+ addbot commands for large sv_maxclients, allegedly
+ caused segfaults in 1.17. Not reproducible.
+ TODO: recover more gracefully from failure to add bot?
+
+ * code/renderer/tr_light.c (R_LightForPoint): Tim Angus
+ reports a crashbug with nolight maps. Also assertion in
+ R_SetupEntityLightingGrid, might want conditional there.
+ DONE: fixed crash on LightForPoint for nolight maps.
+
+ * code/qcommon/qcommon.h: NUM_SERVER_PORTS. A feature
+ request to increase this, or make it more flexible
+ otherwise (Fenris).
+ TODO: id decision on more flexible NUM_SERVER_PORTS.
+
+2001-01-02 Bernd Kreimeier <bk@lokigames.com>
+
+ * code/unix/snapvector.nasm: fixed FPU bit (the current
+ one had reserved bits off, behavior should not change).
+ * code/qcommon/vm_x86.c: fixed symbols (below).
+ * code/unix/ftol.nasm: FPU bits weren't correct (duh).
+ DONE: shoot-though floor (q3dm5)
+ DONE: cursor-in-rect off (TA/Player model selection)
+ Note: in gdb, "disassemble <funcname>" is your friend.
+
+ * code/cgame/cg_public.h: CG_MEMSET is set to 100. In
+ cg_syscalls.asm it's 101. If I change it I get Bad trap 100
+ from the cgame VM code, so the 1.27g "official" VM code
+ uses it.
+
+ * code/unix/linux_common.c: have to fall back to C, the
+ current assembly is buggy...
+ * code/unix/Makefile (linux_common.o): added.
+ Later: also for dedicated. Less portable this way.
+ TODO: C_ONLY for dedicated on non-i386 only?
+
+ * code/qcommon/common.c: do not use memcpy/memset under Linux.
+ * code/unix/linux_common.c: added Andrew's assembly port.
+ TODO: C_ONLY for Com_Memset/Memcpy? Conditionals are fubared.
+
+ * code/qcommon/vm.c (VM_Init): use Win32 defaults (do not
+ use DLL's by default). This exposes DLL rounding errors
+ (damage through floors), and we don't want DLL's used by
+ default anyway.
+ TODO: why vm_ui default of 1?
+
+ * code/botlib/l_precomp.c (SourceWarning): removed assert.
+
+ * code/game/bg_lib.c (acos): defined, but we don't actually
+ use it except where the cg_syscalls.asm trap is used.
+
+ * code/game/g_public.h: missing lots of trap tokens.
+ * code/game/g_syscalls.c: missing lots of traps.
+ * code/game/g_syscalls.asm: more inconsistent hooks, were:
+ equ floor -111
+ equ ceil -112
+ equ testPrintInt -113
+ equ testPrintFloat -114
+ now changed to match cg_syscalls.
+ Note: fixed this in UI earlier, how did this slip through
+ the diffs against SOS?
+
+ * code/game/g_syscalls.c: no acos hook.
+ * code/cgame/cg_syscalls.c: no acos hook.
+ * code/cgame/cg_syscalls.asm: has acos hook as -112
+ Note: report from Tim Angus. The acos function is in bg_lib.c
+ which is linked only into ui (not q3_ui). That means we are
+ using libc acos right now?
+ Note: QVM traps are negative?
+
+ * BSD/Irix: tagged current CVS (not all of the below) as
+ cvs tag id1-27g-loki010102-bsd1
+ for BSD work (Rafael Barrero). Also be used for Irix update.
+
+2001-01-01 Bernd Kreimeier <bk@lokigames.com>
+
+ * SOS: adding the remaining SOS snapshots to CVS.
+ cvs import Quake3_sos sos001201-rc3 pr1-27f-win32-001201-rc3
+ cvs import Quake3_sos sos001202 pr1-27f-win32-001202
+ cvs import Quake3_sos sos001204 pr1-27g-win32-001204-rc4
+ This is the codebase to which the Linux branch has been patched
+ up. I can't verify whether this is identical to the RC4 codebase
+ as the tag doesn't work (but can check against the ZIP file..)
+ cvs import Quake3_sos sos001211 pr1-27g-win32-001211
+ The above snapshot contains a (post-release?) fix to ui_syscalls
+ in ui/ and q3_ui/. This change has been used in Linux (Beta1 and
+ above). At this point, id discouraged further use of SOS due to
+ repository corruption on their end. No further snapshots were
+ taken since.
+
+ * Fenris: since the release of the Beta1 bugs have been
+ maintained at http://fenris.lokigames.com/. I am going to
+ list issues here as they get fixed.
+
+2000-12-21 Bernd Kreimeier <bk@lokigames.com>
+
+ * code/renderer/tr_font.c: graceful silence with old mods?
+ * code/botlib/l_precomp.c (SourceWarning): graceful exit if old mod?
+
+2000-12-20 Bernd Kreimeier <bk@lokigames.com>
+
+ * code/server/sv_ccmds.c (SV_MapRestart_f): see below.
+ * code/qcommon/vm.c: currentVM is 0x0 in VM_ArgPtr.
+ In VM_Call, oldVM was NULL - made conditional the
+ reset of currentVM to oldVM.
+
+2000-12-18 Bernd Kreimeier <bk@lokigames.com>
+
+ * BETA1: closed Linux beta release. Stripped debug
+ and release binaries, DLL's, and pak4.pk3. CVS checkin,
+ will be tagged as
+ cvs tag id1-27g-loki001218-beta1
+ Later: id added a pak5.pk3 to the Win32 point release,
+ added this to the BETA1 best.
+
+ * code/qcommon/vm_x86.c: C37F.
+ * code/unix/snapvector.nasm: C37F.
+ Note: short of any real evidence, I gamble and use max.
+ precision (as well as default Linux precision, but NOT
+ Win32 precision). It seems that precision change is not
+ really an issue (despite Graeme's claim that the cursor
+ in the menu was/is off). I also pick the roundiung behavior
+ that is seemingly used by ANSI and gcc (but possibly not
+ Win32 _ftol depending on build).
+
+2000-12-15 Bernd Kreimeier <bk@lokigames.com>
+
+ * code/unix/Makefile: added snapvector.o
+ * code/unix/unix_shared.c: #if 0'ed the old snapvector code.
+ * code/unix/snapvector.nasm (Sys_SnapVectorCW): two new
+ assembly functions from AndrewH that explicitely set the
+ FPU control word to convert vec3_t, to ensure cross-platform
+ behavior for both DLL and QVM.
+
+ * code/unix/ftol.nasm (Q_ftolC37F): for globals.
+
+ * code/unix/unix_main.c: took out global FPU manipulation.
+ For clarity this should be VM only.
+ * code/qcommon/vm_x86.c: added prototypes for the ftol
+ library. To select a specific behavior for the entire VM,
+ set ftolPtr accordingly.
+ Later: the GCC ftol function of course affect the stack
+ (there is no "declspec naked"). The problem seems to be
+ that the VM never handles the stack in a way compatible
+ to regular gcc C functions. For some odd reason _ftol seems
+ to do the right thing under Win32. All 4 control words
+ implemented at the moment work just fine with the menus.
+
+ * code/unix/ftol.nasm: added a small library of "safe" qftol
+ variations that explicitely set the control word to the
+ relevant (4) possibilities.
+
+2000-12-13 Bernd Kreimeier <bk@lokigames.com>
+
+ * code/qcommon/vm_x86.c: an entire day spent trying to nail
+ the ftol issues. It breaks down like this: id used to use
+ an unsafe (no setting FPU control word) fistp. That seemingly
+ caused subtle physics bugs which nobody cared about in 1.17.
+ They then changed the UI code, and ran into the UI bugs:
+ menu entries shifted to the right, fonts vanishing. Then
+ they switched to using _ftol. Then they had to reproduce
+ the old behavior for the physics code due to public outrage.
+ My original port used a simple (long)float cast, which gcc
+ seemingly compiles to code that does OR 0C00 on whatever
+ current control word (precision unchanged). This breaks the
+ menus. If I use the unprotected fistp instead, which should
+ (Linux 037F default) use "nearest/even", then my menus are
+ correct. That would mean Win32 _ftol in id's compile does
+ the same, only that would require /qifist or some equivalent
+ compile flag, which I can't find. Two disassemblies of _ftol
+ I got from others showed OR 0C00 as part of the default (ANSI)
+ behavior.
+
+2000-12-13 Bernd Kreimeier <bk@lokigames.com>
+
+ * code/game/bg_pmove.c (PmoveSingle): trap_SnapVector.
+ The one true and single call to snap velocity.
+ Note: bspc/map.c:void SnapVector(vec3_t normal)
+ qcommon/cm_patch.c:void CM_SnapVector(vec3_t normal)
+ game/q_shared.h: #define SnapVector(v) {v[0]=((int)(v[0]));...
+
+ * code/client/cl_cgame.c: CG_SNAPVECTOR.
+ * code/server/sv_game.c: G_SNAPVECTOR.
+ Note: these go through trap_SnapVector in syscalls.
+
+ * code/unix/unix_shared.c (Sys_SnapVector): sticking to
+ old Linux version for now...
+ * code/win32/win_shared.c (Sys_SnapVector): changed.
+ Note: Graeme points out this was changed to fix ftol
+ artifacts?
+ TODO: calculate errors for various ftol variants...
+
+ * code/qcommon/vm_x86.c: both the old fistp code (1.17)
+ and the new qftol function apparatently work. Using the
+ ftol.nasm code for now.
+
+ * code/unix/Makefile: DO_NASM and ftol.o.
+
+ * code/unix/ftol.nasm (qftol): created from Mike's SoF
+ replacements, with Andrew's help to satify the VM
+ stack/call requirements.
+ TODO: use Q_ftol herein to replace myftol elsewhere.
+
+ * code/unix/unix_main.c (Sys_ConfigureFPU): SIGFPE.
+ TODO: divide by zero in botlib. Disable this for now.
+ Note: we can't introduce calculation differences between
+ versions, so fixing these will have to wait.
+
+ * code/qcommon/vm_x86.c: two new lines in Win32 branch
+ missing from Linux assembly in AsmCall:
+ mov eax, dword ptr [edi]
+ and eax, [callMask]
+ Added, doesn't seem to affect UI etc. bugs.
+ Later: no FTOL_PTR, use fistp non-IEEE assembly as in old
+ version. This seems to work for Q3 and TA, while qftol
+ (simple cast) does not - for Win32 Graeme says the reverse
+ is true.
+
+ * code/qcommon/vm_x86_old.c: used the old cvs-1.17 version.
+ Two fixes (Hunk_Alloc, Com_Memcpy), and it works:
+ +set vm_game 2 +set vm_ui 2 +set vm_cgame 2
+ UI, cgame and game w/o apparent problems.
+
+2000-12-12 Bernd Kreimeier <bk@lokigames.com>
+
+ * code/unix/Makefile: cleanup of redundant flags.
+ Removed bogus MALLOC_CHECK (note to self: export MALLOC_CHECK_=2).
+ Also DO_SHLIB_CC on all UI DLL's.
+ Added and removed DEBUG_VM flag.
+ TODO: figure out whether Zoid did UI this way intentionally.
+ Note: this seemingly fixed the botimport problem, although
+ most of the changes were just redundant CFLAGS removed. Given
+ our wanker toolchain, should have been more paranoid. All
+ DLL's can now be used w/o apparent problems.
+
+ * code/server/sv_main.c: gvm init.
+ * code/server/sv_game.c: gvm assertions.
+ * code/unix/unix_main.c (Sys_LoadDll): print vmMain
+ Note: top no avail. There is some odd ld/gdb problem here
+ that prevents examining globals and obfuscates part of
+ the stack between VM_Call and lower level code, through
+ G_InitGame. This is not just DLL's being loaded and unloaded.
+ Wromg flags during build? The vmCvar for "bot_developer"
+ ends up overlapping global botimport in memory, which
+ thus zero-fills part of the function pointer table.
+
+ * code/server/sv_bot.c (SV_BotInitBotLib): this (by way of
+ GetBotLibAPI) is responsible for setting botimport, which,
+ if using the game DLL, is not properly set up. Called in
+ SV_Init().
+
+ * code/game/q_shared.c: Q_strncpyz does zero padding (duh).
+ Note: calls strncpy, which does a zero fill up to destsize.
+ If destsize exceeds memory size, zero padding will overwrite
+ adjacent memory. Suspicion was this happend to botimport.
+
+ * code/qcommon/cvar.c: possible problem in Q_strncpyz call.
+
+ * code/botlib/be_ai_weap.c (weaponinfo_fields): made this static.
+ Note: it seems that the "number" string got replaced by
+ p def.fields[0]
+ {name = 0x40000000 "\177ELF\001\001\001", offset = 2, type = 50, ..
+ Memory corruption?
+
+ * code/game/inv.h (WEAPONINDEX_GAUNTLET): defined here.
+ * botfiles/weapons.c (Gauntlet): the baseq3/qagamei386.so parser
+ breaks here:
+ number WEAPONINDEX_GAUNTLET
+ * code/botlib/l_precomp.c (SourceWarning): added assertion to
+ trap botlib parsing problem..
+
+ * RC1: for beta test. Using my own vm/ui.qvm files in this case.
+ TODO: Setup with nouninstall.
+ TODO: fix game DLL/ botlib setup problem (so all DLL's work)
+ TODO: SIGFPE
+ TODO: profile?
+
+ * code/unix/Makefile (ai_vcmd.o): added to game DLL linkage.
+ How the fuck did this happen?
+ DONE: "qagamei386.so: undefined symbol: BotVoiceChat_Defend"
+
+ * TEST: +set vm_ui 2 (vm_x86, not interpreter). Breaks!
+ Further: qagame had undefined, but seemingly gets reloaded
+ second try (I hate the Linux linker).
+ * TODO: never reload fail DLL, abort engine
+
+
+2000-12-11 Bernd Kreimeier <bk@lokigames.com>
+
+ * TEST: recompile QVM/DLL and executable to test new UI code.
+ The UI QVMs from the paks still do not work.
+
+ * SOS: changes in UI code!
+ * code/q3_ui/ui_public.h: this file is deprecated
+ Note: e.g. it does not contain the background track calls.
+ * code/ui/ui_public.h: the uiImport_t enum here determines
+ the values.
+ * code/ui/ui_syscalls.asm: same as q3_ui now, were:
+ equ floor -111
+ equ ceil -112
+ * code/q3_ui/ui_syscalls.asm: these are now switched, were:
+ equ trap_S_StartBackgroundTrack -63
+ equ trap_S_StopBackgroundTrack -64
+ The new values match the ui/ equivalent. Also, floor (-108)
+ and ceil (-109) are different in ui/.
+
+ * CVS: going to check in this snapshot and tag it as
+ cvs tag id1-27g-loki001209-rc4
+ Presumed equivalent to SOS tag "1.27g RC4" (master). As I can't
+ obtain the tagged code using SOS (neither Win32 nor Linux client)
+ I can only guess.
+
+ * TEST: use my own VM code, ion baseq3/vm/*.qvm and missionpack/vm/.
+ This works - in other words, the menu bug seems in the UI code, and
+ is fixed in my codebase.
+
+ * TEST: make release.
+ Note: I can postpone DLL specific problems. Bad performance is not
+ as important as outright bugs. Thus the UI QVM issue is the only
+ one that stops me from creating an RC.
+ TODO: Q3 UI QVM code from pak file does not work (neither does TA).
+ TODO: sound with video playback still awful. Threaded sound, I guess.
+ TODO: ERROR: couldn't open demos/DEMO002.dm3.dm_48 (same demo001.dm3.dm_48)
+
+ TODO: do not show Q3 demos in TA menu?
+ TODO: new demos for Q3? Or at least error message?
+
+ * code/game/bg_lib.c: itrinsics excluded by Q3_VM (another -O
+ compile). Uninitialized variable.
+ * code/unix/Makefile: -O for uninit on patched code. Also shortcuts.
+ TODO: DC_ONLY seems an obsolete flag, used in Makefiles, not source.
+
+ * TEST: +set sv_pure 0 +set vm_game 1 +set vm_cgame 1 +set vm_ui 0
+ Turns out that the pak0.pk3 UI QVM code is seemingly broken in TA
+ and Q3, but my UI DLL is not. In reverse, the QVM game/cgame for
+ Q3 seems to work quite well (including bots). The TA game/cgame
+ also works, including botlib init.
+ TODO: BotLib Init using game DLL gives:
+ TODO: Error: file weapons.c, line 38: unknown structure field number
+ TODO: Fatal: couldn't load the weapon config
+ TODO: Error: BotLoadMap: bot library used before being setup
+
+ * TEST: checked the rc4winstlr.zip CD tree against
+ my test install. baseq3/pak4.pl3 and missionpack/pak0.pk3
+ are identical, but I finally recognized that there was
+ a missionpack/pak1.pk3 not in the final install - left over
+ from an earlier update from id. Doesn't seem to affect the
+ DLL based runs at all.
+ Note: I still do not have the final CD snapshot Robert
+ promised me mid last week, they haven't even fixed the
+ FTP account they took down. Communication with id is as
+ abyssmal as ever.
+
+2000-12-08 Bernd Kreimeier <bk@lokigames.com>
+
+ * TEST: running with RC4 data files.
+ TODO: "bot library used before setup" (Q3+TA)
+ TODO: Q3 old mods wreak havoc (graceful bounce)
+ TODO: supress "FreeType code not available" in renderer
+ TODO: can't move in Q3
+ TODO: items flicker in Q3
+ TODO: no decals in Q3
+ TODO: VM UI code still broken (Q3+TA)
+ TODO: sound code is awful
+ TODO: video playback inferior to earlier builds
+
+ * code/q3_ui/ui_local.h: prototype trap_VerifyCDKey(..)
+ * code/game/g_active.c ( StuckInOtherClient): TA only.
+ * code/cgame/cg_draw.c: 4x unbalanced `#endif' - from patch?
+ * code/null/null_client.c (CL_CDKeyValidate): dummy added.
+ * code/qcommon/common.c: Q_acos missing, changed conditionals
+
+ * code/qcommon/vm_x86.c: unreacheable _asm instruction that
+ gcc doesn't quite like... #if 0'ed for now
+ TODO: understand _asm { mov eax,[ebx] }, fix it for gcc
+
+ * TEST: compile...
+
+ * code/ui/ui_main.c: full REJECT. Manual merge.
+ Note: preserved debug_protocol lines, who knows what it's good for.
+
+ * code/qcommon/files.c: REJECT. SafeMode, demo server FS_Restart.
+
+ * code/client/snd_mem.c: REJECT: $SOS$.
+ * code/client/snd_dma.c: REJECT: $SOS$.
+ * code/client/cl_cin.c: REJECT. com_timescale, $SOS$.
+
+ * code/cgame/cg_draw.c: REJECT. Lots, but virtually all either
+ float postfix (on some, not all places), or #ifndef MISSIONPACK
+ that I had already put in during -Werror (conditional unused).
+
+ * code/cgame/cg_consolecmds.c: REJECT. id commented unused code
+ that I had #if 0'ed earlier.
+
+ * code/game/: three REJECT for $SOS$.
+ * code/botlib/: lots REJECT for $SOS$.
+
+ * Patch: patching up from demo source.
+ ln -s sos001204-rc4 work
+ diff -urbB sos001122-demo/ work > work.diff
+ ln -s cvs-1.27b/ work
+ patch -p0 < work.diff > work.patch
+ find cvs1.27b/ -name '*.rej' -print
+
+ * CVS: going to check in this snapshot and tag it as
+ cvs tag id1-27b-loki001208-demo
+ Then patching up to RC4, as of sos001204-rc4 (no changes since,
+ should be equivalent to SOS tag "1.27g RC4" (raduffy), i.e. master.
+
+ * TEST: installed demota/ from Win32 distribution. Binary
+ fails claiming "Corrupted pak0.pk3". Abandoned.
+ Note: a Linux demo for Q3TA has no priority. Most important is
+ the Q3A point release in time for Q3TA hitting shelves, followed
+ by testing for Q3TA. The source is in CVS and tagged (see above)
+ in case a demo matching the released files has to be provided
+ later.
+
+
+2000-12-07 Bernd Kreimeier <bk@lokigames.com>
+
+ * TEST: compile and link - succeeds.
+
+ * code/ui/ui_main.c: UI_StopServerRefresh now uaws.
+ New unused variables.
+
+ * code/unix/unix_main.c: added Sys_LowPhysicalMemory() stub.
+ TODO: write Linux equivalent to GlobalMemoryStatus.
+
+ * code/qcommon/common.c: Com_Memset/Com_Memcpy. Neither assembly
+ nor C versions included if not on Win32 i386.
+ TODO: using/porting assembly?
+
+ * code/qcommon/files.c: unused variable.
+ TODO: fs_scrambledProductId unused if 0 for now.
+ Note: -DFS_MISSING for id's pak cleanup, not used.
+
+ * TEST: compile and link - fails.
+
+ * code/macosx/Client/Makefile.postamble: empty ORIG.
+ * code/macosx/Client/Makefile.preamble: ORIG. $(BOTLIB_OBJS) added.
+
+ * code/server/sv_client.c: ORIG. Com_Memset.
+ * code/renderer/tr_shader.c: ORIG. Com_Memset, CIN_Shader.
+ * code/qcommon/vm_x86.c: ORIG. Com_Memcpy.
+ * code/qcommon/unzip.c: REJECT. Com_Memcpy, $SOS$.
+ * code/qcommon/qcommon.h: ORIG. PROTOCOL 47, plus Sys_LowPhysicalMemory.
+ * code/qcommon/md4.c: Com_Memset,Com_Memcpy (ORIG).
+ * code/qcommon/files.c (Sys_ConcatenateFileList): REJECT.
+ Our additons threw it off, plus $SOS$.
+ * code/qcommon/common.c: they fixed same unused variables (REJECT).
+
+ * code/ui/ui_shared.c: additions (ORIG).
+ * code/ui/ui_gameinfo.c: COM_Compress added (ORIG).
+ * code/ui/ui_atoms.c: print statements removed (ORIG).
+ * code/ui/ui_main.c (UI_DoServerRefresh): REJECT on comment edit...
+
+ * code/game/g_cmds.c (Cmd_VoiceTaunt_f): logic changed heavily. ORIG.
+ * code/game/q_shared.h: Q3_VERSION "Q3 Team Arena Demo 1.27b"
+ plus Com_Memset, Com_Memcpy, CIN_shader, COM_Compress.
+ * code/game/g_main.c: Cvar change only
+ * code/game/ai_dmq3.c: $SOS$.
+
+ * code/client/snd_mix.c: Com_Memset
+ * code/client/client.h: additions (ORIG).
+ * code/client/snd_mem.c: see below.
+ * code/client/snd_dma.c: $SOS$ (CVS keyword).
+
+ * code/client/cl_cin.c: they removed unused (REJECT).
+ * code/cgame/cg_servercmds.c: ORIG. compress, noTaunt etc.
+ * code/cgame/cg_main.c: ORIG. Conditonal branch, COM_Compress.
+ * code/cgame/cg_consolecmds.c: ORIG. Cvar values changed.
+ * code/cgame/cg_draw.c (CG_DrawTeamBackground): ORIG.
+ no reject but *.orig file created. I just mark spots were
+ code changed after verifying the patch succeeded.
+
+ * code/cgame/cg_event.c: fixed reject (REJECT).
+ * code/botlib/: all *.rej here due to SOS/CVS $Keyword$.
+ TODO: preserve SOS comments/rev history somehow.
+
+ * Patch: patching up to demo source.
+ ln -s sos001122-demo work
+ diff -urbB sos001119/ work > work.diff
+ ln -s cvs-1.26/ work
+ patch -p0 < work.diff > work.patch
+ find cvs1.26/ -name '*.rej' -print
+
+ * CVS: going to check in this snapshot and tag it as
+ cvs tag id1-26w-loki001207
+ to prepare for upgrading to RC4. I have already made
+ many more changes than I wanted to w/o getting any
+ closer to pinpointing the problem, I might as well
+ patch up to id's more current sources.
+
+ * code/botlib/be_interface.c: initialize by memset. Turns
+ out that this fails in Export_BotLibSetup on BotSetupWeaponAI
+ loading "weapons.c" (from the pak, presumably) with an unknown
+ structure field number. Mismatch of datafiles vs. source again.
+
+ TODO: id replaced memsets in later source.
+ TODO: have memsets on all exports and imports.
+
+ * SOS: RC4 source should be tagged "1.27g RC4" (raduffy).
+ Unfortunately the Linux client doesn't care a bit. Show
+ History does work if from/to date differ by at least a
+ day, and it shows the tag on code/ (only that subtree),
+ but recursive get aborts halfway.
+ Manual: http://www.sourcegear.com/SOS/Doc/
+
+2000-12-06 Bernd Kreimeier <bk@lokigames.com>
+
+ * TEST: accepting missing shaders now. No bots, but I can
+ actually enter the game and play (more than can be said for
+ classic Q3 right now).
+ TODO: Error: BotStartFrame: bot library used before being setup
+
+ * code/renderer/tr_shader.c: took out assertion for now...
+ * TEST: now missiopack/cgame loads
+ TODO: tr_shader.c:2275: R_FindShaderByName: failed
+ TODO: searches ui/assets/3_cursor2.TGA, has ui/assets/3_cursor3.tga
+
+ * code/unix/Makefile (MPCGOBJ): ui_shared.o (duh).
+ DONE: /cgamei386.so: undefined symbol: PC_Float_Parse
+
+ * code/botlib/be_ai_goal.c: initialize campspots etc. This
+ might or might not fix this one (didn't get back to gdb due
+ to mouse-only navigation).
+ DONE: 0x80d1d5b in BotFreeInfoEntities () at be_ai_goal.c:447
+
+ * TEST: this time with missionpack/cgame loading... noy
+ TODO: TA menu blocked after end of intro movie
+ TODO: console in_mouse 1 doesn't grap pointer even on vid_start?
+
+ * code/cgame/cg_newdraw.c: -Werror.
+ * code/unix/Makefile (MPCGOBJ): cg_newdraw.o was missing (duh).
+ DONE: missionpack/cgamei386.so: undefined symbol: CG_OwnerDrawVisible"
+
+ * code/ui/ui_shared.c:1309 assign after bail on NULL.
+ DONE: segfault in Item_SetFocus (item=0x0, x=0, y=0)
+
+ * TEST: new set of DLL's (this time hopefully correct).
+ All baseq3/ DLL's load, as does the missionpack/ UI DLL.
+ The menus now work in both (TA seems mouse-only on everything
+ but "Quit"). Segfault on delayed TA "Quit" (stack fubared):
+ #5 0x809fc28 in VM_Call (vm=0x88408a0, callnum=3) at ..//qcommon/vm.c:617
+ #6 0x805aafc in CL_KeyEvent (key=9, down=qtrue, time=128644) cl_keys.c:1194
+ TODO: TA menu's w/o mouse?
+ TODO: Win32 goes submenus but does not unfold
+ TODO: Linux does not go submenus
+
+ * code/ui/ui_main.c: see below.
+ TODO: LCC gets fits - operands of = have illegal types
+ TODO: 'pointer to const unsigned char' and 'pointer to const char'
+ * code/ui/ui_shared.c: see below.
+ * code/ui/ui_gameinfo.c: see below.
+ * code/ui/ui_atoms.c: see below.
+ * code/game/g_bot.c: more cruft.
+ * code/cgame/cg_draw.c: loads of functions modified for
+ MISSIONPACK that aren't used at all for MISSIONPACK anymore.
+ Development relics.
+
+ * code/cgame/cg_consolecmds.c: -Werror.
+ Note: due to Makefile error never ever compiled...
+
+ * code/unix/Makefile: fixed various dependency errors
+ for game and ui library.
+ TODO: create a new Makefile with patsubst and rules.
+ TODO: why C_ONLY in the i386 dedicated server?
+
+ * code/unix/unix_main.c: use dlerror() excessively.
+ Littered more unused DLL related functions with assert(0).
+ TODO: clean up Sys_Load/UnloadDll (a real mess)
+ TODO: remove Zoid code cruft (unused per-DLL functions)
+
+ * code/game/bg_misc.c: changed G_Printf for Com_Printf.
+ This was undefined in baseq3/uii386.so preventing loading.
+
+ * TEST: +set sv_pure 0 +set vm_game 0 +set vm_cgame 0 +set vm_ui 0
+ Note: so far I used only the game DLL.. duh.
+ UI DLL fails to load: missing G_Printf.
+
+ * code/unix/Makefile: -DMALLOC_CHECK in addition to
+ the -DZONE_DEBUG I have used since switching to calloc.
+ Using MALLOC_CHECK=1 for now, might use 2 if something
+ comes up.
+
+ * code/renderer/tr_init.c (GL_SetDefaultState): it does get
+ called, but does not show up in the log.
+
+ * TEST: tried executing a script - get bounced.
+ TODO: is there any way to jump into a map?
+ TODO: cl_cinematics 0 (supress all fullscreen RoQ)
+ Next: used r_logfile 200 in Win32 (RC4) and Linux.
+ There is a buckload of setup code seemingly not done
+ at all in Linux? Either that, or logging is enabled
+ with a delay in Linux.
+
+ * code/unix/linux_glimp.c: fixed autorepeat (H2/Fakk2 way).
+
+2000-12-05 Bernd Kreimeier <bk@lokigames.com>
+
+ * code/renderer/tr_mesh.c: added assert there.
+ * TEST: menus and in-game drawing are just as they were with
+ the initial SOS001119 port. In addition:
+ R_AddMD3Surfaces: no such frame 0 to -2147483477
+ for 'models/players/xaero/upper.md3'
+ R_AddMD3Surfaces: no such frame -2147483477 to 171
+ R_AddMD3Surfaces: no such frame 171 to -2147483498
+ ad nauseam (used as my player model).
+ Triggered: haveing a trRefEntity_t *) 0x41dbbd00 with
+ frame = -2147483477. Might be a red herring (PRINT_DEVELOPER),
+ ignore for now.
+
+ * code/ui/ui_main.c: missing return.
+ * code/ui/ui_shared.c: excess byte in initializer (which gcc
+ did not caught, but LCC did). Also LCC complains about
+ missing returns, but gcc doesn't (neither says unreacheable
+ code though). If necessary (MsVC?) guard with Q3_VM.
+
+ * code/q3_ui/ui_ingame.c: see below.
+ * code/q3_ui/ui_atoms.c: voidfunc_f. LCC warns about conversion
+ from `pointer to void' to `pointer to void function(void)'
+ being compiler dependent. Casting NULL. Guess what, doesn't fix
+ it either.
+ TODO: do not use these cursed scripts to generate VM code,
+ we do not have proper rules for LCC/q3asm, thus the files never
+ get updated.
+
+ * code/unix/Makefile: for paranoia's sake recreated the 1.17
+ compile for the UI DLL (where only q_shared/math were actually
+ compiled as DO_SHLIB_CC.
+ Later: switched to different gcc.
+
+ * STATIC: remaining problems are vmMain (same entry point for all
+ DLL's), could use cgMain, uiMain and gMain here for HARD_LINKED.
+ Note: I don't think id has used this in ages.
+ Plus all the collisions in *_syscalls.c, which simply can't be
+ fixed cheaply. None is the superset of 2 others, neither seems
+ w/o overlap to others. Full stop.
+
+ * code/botlib/be_aas_move.c: see below.
+ * code/game/ai_dmq3.c: VEC_UP/DOWN, MOVEDIR_UP/DOWN now static.
+ See also game/g_utils.c for existing static duplicates.
+
+ * code/game/q_shared.h: #define stricmp strcasecmp
+ * code/unix/Makefile: no mo' -Dstricmp=strcasecmp, see q_shared.h
+ Also: no mo' -I/usr/include/glide, no FX
+ TODO: are we building against system GL headers? ../Mesa/?
+
+ * code/q3_ui/ui_atoms.c: comment on duplication
+ * code/cgame/cg_drawtools.c: use UI/CGAME_HARD_LINKED on UI duplicates
+ TODO: does this UI_ code in cg_drawtools/ui_atoms belong into ui_shared?
+
+ * code/unix/Makefile: use -DQ3_STATIC
+ * code/game/q_shared.h (*_HARD_LINKED): trigger on Q3_STATIC
+ Later: collision between UI and CGAME is still there. This fixed
+ the Com_Error, Com_Printf issues though
+
+ * code/unix/Makefile ($(B)/q3static/ai_vcmd.o): this file was
+ missing, hence undefined symbol.
+ ($(B)/baseq3/game/ai_vcmd.o): same here.
+ ($(B)/missionpack/game/ai_vcmd.o): same here.
+
+ * STATIC: cg_syscalls.c, g_syscalls.c and ui_syscalls.c alias.
+ Multiply defined symbols:
+ Com_Error, Com_Printf
+ VEC_UP, VEC_DOWN
+ MOVEDIR_UP, MOVEDIR_DOWN
+ vmMain
+ dllEntry
+ PASSFLOAT
+ trap_Error
+ trap_Milliseconds
+ trap_Argc
+ trap_Argv
+ trap_FS_FOpenFile
+ trap_FS_Read
+ trap_FS_Write
+ trap_FS_FCloseFile
+ trap_FS_GetFileList
+ trap_R_RegisterModel
+ trap_R_RegisterSkin
+ trap_R_RegisterFont
+ trap_R_RegisterShaderNoMip
+ trap_R_ClearScene
+ trap_R_AddRefEntityToScene
+ trap_R_AddPolyToScene
+ trap_R_AddLightToScene
+ trap_R_RenderScene
+ trap_R_SetColor
+ trap_R_DrawStretchPic
+ trap_R_ModelBounds
+ trap_UpdateScree
+ trap_S_StartLocalSound
+ trap_S_RegisterSound
+ trap_Key_IsDown
+ trap_Key_GetCatcher
+ trap_Key_SetCatcher
+ trap_GetGlconfig
+ trap_PC_AddGlobalDefine
+ trap_PC_LoadSource
+ trap_PC_FreeSource
+ trap_PC_FreeSource
+ trap_PC_ReadToken
+ trap_PC_SourceFileAndLine
+ trap_S_StopBackgroundTrack
+ trap_S_StartBackgroundTrack
+ trap_RealTime
+ trap_CIN_PlayCinematic
+ trap_CIN_StopCinematic
+ trap_CIN_RunCinematic
+ trap_CIN_DrawCinematic
+ trap_CIN_SetExtents
+ trap_MemoryRemaining
+ trap_SendConsoleCommand
+ trap_Cvar_Register
+ trap_Cvar_Update
+ trap_Cvar_Set
+ trap_Cvar_VariableValue
+ trap_Cvar_VariableStringBuffer
+ trap_RealTime
+ trap_SnapVector // used in game/bg_*.c, needs conditional
+ More aliasing between ui_atoms.c and cg_drawtools.c:
+ UI_DrawBannerString
+ UI_ProportionalStringWidth
+ UI_ProportionalSizeScale
+ Undefined symbol: ai_team.o: In function `FindHumanTeamLeader':
+ ai_team.c:1899: undefined reference to `BotVoiceChat_Defend'
+ Note:
+
+ * code/game/g_main.c: unused.
+ * code/game/g_arenas.c: unused.
+ * code/game/ai_team.c: init.
+ * code/game/ai_dmnet.c: /* in comment (odd).
+ Note: why do these come up now but not earlier?
+ TODO: the make dependencies might target wrong files.
+
+ * code/unix/Makefile (TARGETS): added q3static.
+ Note: this is baseq3/
+
+ * TEST: +set r_logfile 100. It seems that the addition of
+ code (add an assertion etc.) changes the behavio of the binary.
+ The intro cinematics code seems to suffer first - didn't play,
+ then played, then (another assert added) doesn't play. Watch
+ out for (missionpack):
+ UI_CIN_PlayCinematic
+ SCR_PlayCinematic( mpintro.roq )
+ trFMV::play(), playing mpintro.roq
+ Also fails to exit cleanly: break gives
+ #0 0x401919ee in __select ()
+ #1 0x400bbcb8 in __DTOR_END__ ()
+ #2 0x4004baa1 in _XSend ()
+ #3 0x452b009f in GLXRenderFlush ()
+ #4 0x804ce0c in _XRead ()
+ #5 0x40680813 in ?? ()
+ Stack is corrupted.
+ Note: ~/.q3a/gl.log
+ TODO: write per-frame files (see Heretic2)
+ TODO: add Heretic2 QGL (more detail)
+
+ * code/unix/linux_qgl.c (QGL_EnableLogging): fixed countdown
+ (i.e. propagated changes from win32/, see Fakk2).
+
+ * code/unix/linux_glimp.c: fixed QGL_EnableLogging argument
+ to avoid cast error (always qfalse).
+
+ * code/unix/Makefile (DEBUG_CFLAGS): use ZONE_DEBUG.
+
+ * code/qcommon/common.c: replaced malloc with calloc calls.
+
+ * code/q3_ui/ui_local.h: have to use ui/ui_public.h
+ * code/cgame/cg_servercmds.c: requires ../ui/menudef.h
+
+ * code/cgame/cg_consolecmds.c: ui/ui_shared.h is unique.
+ * code/q3_ui/ui_public.h: make sure this won't be compiled.
+ * code/client/client.h: we have to include ui/ui_public.h.
+ Note: id is obviously maintaing only the ui/ headers, so the
+ headers in q3_ui/ are deprecated.
+
+ * code/renderer/tr_shader.c: added assertions (see Ryan's Fakk2
+ problems with missing shaders).
+
+ * code/game/g_cmds.c: below.
+ * code/game/ai_vcmd.c: below.
+ * code/game/ai_team.c: below.
+ * code/game/ai_dmnet.c: below.
+ * code/game/ai_dmq3.c: below.
+ * code/game/ai_chat.c: below.
+ * code/game/ai_cmd.c: ../../ui/menudef.h (new Q3TA script directory).
+
+ * code/cgame/cg_newdraw.c: make sure it won't compile w/o MISSIONPACK.
+
+ * code/cgame/cg_servercmds.c: below.
+ * code/cgame/cg_event.c: below.
+ * code/cgame/cg_consolecmds.c: below.
+ * code/client/keys.h: below.
+ * code/client/client.h: below.
+ * code/q3_ui/ui_local.h: include from ../q3_ui/ not ../ui/.
+ Note: id seems to intentionally use the header from the new ui/.
+
+ * Makefile: checked -I$(UIDIR), there is no such. That means all
+ files include directly, which means all (including Q3) are using
+ the new ui/ headers.
+
+2000-12-04 Bernd Kreimeier <bk@lokigames.com>
+
+ * RC4: released as 362101115 Dec 4 11:40 TA_Q3A_RC4.zip
+
+ * TEST: the corrupted menu problem is back :-(. Looks like I am in
+ for a static link next.
+
+ * code/unix/Makefile (clean2): fixed (not all new OBJ covered).
+ * code/q3_ui/ui_teamorders.c: -Werror.
+ * code/q3_ui/ui_team.c: -Werror.
+ * code/q3_ui/ui_qmenu.c (Bitmap_Draw): -Werror.
+ * code/q3_ui/ui_mods.c (UI_Mods_LoadModsFromFile): unused. -Werror.
+ * code/q3_ui/ui_controls2.c: -Werror.
+ * code/q3_ui/ui_atoms.c: -Werror
+ * code/null/null_client.c: -Werror.
+ * code/unix/linux_joystick.c: -Werror.
+ * code/unix/linux_glimp.c: -Werror.
+ * code/unix/linux_qgl.c: -Werror.
+ * code/unix/unix_shared.c: -Werror.
+ * code/unix/unix_net.c: -Werror.
+ * code/unix/linux_local.h: added missing prototypes.
+ * code/unix/unix_main.c: -Werror. Includes linux_local.h
+ * code/jpeg-6/jdmainct.c: see below.
+ * code/jpeg-6/jcmainct.c: variables called "main" (*moan*)
+ * code/jpeg-6/jcdctmgr.c (forward_DCT): -Werror.
+ * code/botlib/l_script.c (PS_ReadLiteral): -Werror
+ * code/botlib/l_precomp.c (PC_AddBuiltinDefines): -Werror.
+ * code/botlib/be_interface.c: -Werror.
+ * code/botlib/be_aas_reach.c: -Werror
+ * code/botlib/be_aas_cluster.c: -Werror
+ * code/game/be_aas.h: -Werror.
+ Note: MrElusive accumulates a lot of code history in nested comments,
+ which gcc doesn't like at all. #if 0'ed to avoid.
+ * code/qcommon/vm_interpreted.c: -Werror.
+ * code/qcommon/unzip.c: -Werror.
+ * code/cgame/cg_servercmds.c: -Werror.
+ * code/cgame/cg_main.c: -Werror.
+ * code/cgame/cg_drawtools.c: -Werror.
+ * code/game/bg_misc.c: -Werror.
+ * code/game/be_ai_move.h (bot_avoidspot_s): added.
+ * code/botlib/be_ai_move.c: removed typedef struct bot_avoidspot_s
+ * code/client/snd_mix.c: -Werror.
+ * code/qcommon/md4.c: -Werror.
+ * code/qcommon/common.c: -Werror.
+ * code/client/cl_keys.c: -Werror.
+ * code/client/cl_cin.c: -Werror, init local variables.
+ * code/unix/Makefile: -Werror. need -O for -Wall for uninitialized
+ Note: the above is the list of files that got touched during a pass
+ with -g -O -Werror -Wall flags (in the hope of finding uninitialized
+ memory and ambiguous statements). Most of the above are simply
+ unused variables (or even code).
+
+ TEST: RC3 data files, but DLL's.
+ TODO: TA gets stuck in initial sound, doesn't play cinematics (sometimes)
+ TODO: Q3 intro movie looses sound after Sarge gets teleported
+ TODO: Q3 ingame renders world, weapon, muzzleflash, hud, can shoot,
+ TODO: but no movement, hud background is fubared.
+
+ * code/cgame/cg_main.c: cg_singlePlayerActive
+
+ * code/q3_ui/ui_login.c: doesn't seem to be used?
+ * code/game/g_rankings.c (G_RankRunFrame): doesn't seem to be used.
+ * code/q3_ui/ui.sh: disabled this.
+ * code/q3_ui/q3_ui.sh: changed include path to ../q3_ui/ (duh).
+
+ * code/game/game.sh: changed include path to ../q3_ui/ which
+ is not in the Win32 batch file.
+ * code/cgame/cg_rankings.c: this does not seem to be included.
+ * code/cgame/cgame_ta.sh: added -DCGAME. Also added cg_syscalls.c
+ to build (also missing in Win32).
+
+ * code/cgame/cgame.sh: added -DCGAME (see cgame.bat). Also
+ changed include path to ../q3_ui/ which is not in the Win32
+ batch file. Also added cg_syscalls.c to build (missing in
+ Win32).
+
+2000-12-01 Bernd Kreimeier <bk@lokigames.com>
+
+ * RC3: released as of sos001201 / Q3 1.27f
+
+ * code/unix/Makefile: more fixes with clean build. The
+ changes made fix the menu rendering for Q3 but not TA.
+ Ingame graphics still broken.
+
+ * code/game/game_ta.sh: created. Use game_ta.q3asm here.
+ * code/game/game.sh: no -DMISSIONPACK
+ * code/game/game_ta.q3asm: CR/LF, /.
+
+ * code/cgame/cgame_ta.sh: created. Use cgame_ta.q3asm here.
+ * code/cgame/cgame.sh: no -DMISSIONPACK. No cg_newdraw, ui_shared.
+ * code/cgame/cgame.q3asm: No cg_newdraw, ui_shared.
+ CR/LF, /, cg_newDraw, and the output path/name.
+
+ * code/q3_ui/q3_ui.q3asm: output to ui not q3_ui...
+
+ * code/cgame/cg_event.c: cg_singlePlayerActive used here.
+ TODO: guard by MISSIONPACK
+ * code/cgame/cg_local.h: named q3print_t enum. Cvar
+ cg_singlePlayerActive for both Q3 and TA.
+
+
+ * code/unix/Makefile: cleanly separate B/baseq3/ and
+ B/missionpack/ subtrees during build. While new and old
+ UI are in separate directories, the cgame/ and game/
+ are shared, with conditional -DMISSIONPACK compile
+ and different files includeds (cd_draw, cg_newdraw).
+ That means twice the number of targets (3 DLL's, 3 QVM's,
+ times two), and different build rules.
+ TODO: carefully check Win32 build for (other) conditionals
+ TODO: carefully check Win32 build for link lists
+
+ * CVS: ui/, code/ui, botfiles/ and subdirectories are added.
+ The code/macosx/ directory turned out to be a real pain that
+ had to be edited manually, throwing out CVS/ directories in
+ the tree that had been created by SOS as they are in id's
+ repository:
+ code/macosx/Client/CVS
+ code/macosx/Client/PBUserInfo/CVS
+ code/macosx/Client/Quake3.nib/CVS
+ code/macosx/Common/CVS
+ code/macosx/DedicatedServer/CVS
+ code/macosx/DedicatedServer/PBUserInfo/CVS
+ Now tagged
+ cvs tag id1-26y-loki001119
+ TODO: there are several new files not yet linked?
+
+ * ChangeLog: merged the Changelog from the bk00119 working
+ branch (initial Q3TA port) based on sos001119 snapshot. Also
+ merged the source tree with cvs-1.17.
+ In the ChangeLog below *** MISSIONPACK *** indicates work
+ that was done on the branch (code-sos/ prefix in files).
+ The cvs update of this will be tagged with
+ cvs tag id1-26y-loki001119
+ Use this tag to hunt for possible Linux fixes that got lost
+ (i.e. got dropped by id since id000516 and were thus not in
+ sos001119, but did not show in diff id000516 cvs1-17).
+ New directories in CVS: botfiles/, ui/.
+ Missing from SOS/Missionpack: SDK directories.
+ common, lcc, libs, q3asm, q3data, q3map, q3radiant.
+
+
+ * ssreport.txt: below.
+ Note: watch for files called "ssreport.txt", that's id ChangeLog.
+ * ui/ui_syscalls.asm: below.
+ * q3_ui/ui_syscalls.asm: below.
+ * game/g_syscalls.asm: below.
+ * cgame/cg_syscalls.asm: below.
+ * bspc/linux-i386.mak: below.
+ * bspc/lcc.mak: below.
+ * botlib/linux-i386.mak: below.
+ * botlib/lcc.mak: below.
+ * A3D/a3d_console_variables.txt: CR/LF issue (minimize diffs).
+
+ * CVS: the checked bk001119 work copy of the sos001119 initial
+ checkout (completed with everything in the SOS "Missionpack"
+ tree, i.e. botfiles/ and botfiles.* added), copied over the
+ cvs-1.17 checkout.
+ Note: in these cases, BEWARE ui -> q3_ui/ links, and different
+ ChangeLogs. Also "make clean" helps.
+
+ * unix/unix_net.c: below.
+ * unix/unix_main.c: below.
+ * unix/matha.s: below.
+ * unix/linux_qgl.c: below.
+ * unix/linux_glimp.c: see also linux_joystick.c.
+ * server/sv_client.c: below.
+ * renderer/tr_surface.c: below.
+ * renderer/qgl.h: below.
+ * qcommon/qcommon.h: below.
+ * qcommon/files.c: below.
+ * qcommon/common.c: below.
+ * q3_ui/ui_demo2.c: below.
+ * mac/mac_net.c: below.
+ * mac/mac_glimp2.c: below.
+ * game/surfaceflags.h: below.
+ * game/bg_lib.c: checked against id00516/cvs-1.17a diff.
+ * bspc/bspc.c: TH_AASToTetrahedrons call removed since id000516.
+ Note: our final compare of id000516 against cvs-1.17a, making sure
+ that all these differences are in bk001119 (initial Q3TA port).
+ If id branched the Q3TA base off before id000516 we might be screwed.
+ Note: I do not diff against bk000520, which had some minor changes
+ against id000516 (check VectorArrayNormalize, OTConfiguration), which
+ seem consistent with me taking a pre-id000516 source snapshot for that
+ working branch.
+
+2000-11-30 Bernd Kreimeier <bk@lokigames.com>
+
+ * TEST: compiled using the symbolic link ui/ -> q3_ui/.
+ Had to undo one CVS change, regarding
+ code/cgame/cg_syscalls.asm
+ code/game/g_syscalls.asm
+ code/q3_ui/ui_syscalls.asm
+ These files are neither generated by Win32 cgame.bat
+ nor cgame.sh (etc.), thus seemingly maintained by hand.
+ cvs tag pr1-17-loki001130b
+ should be used if somebody needs this 1.17 snapshot
+ (which, remember, is post-release, with additional fixes).
+ Later:
+ cvs tag pr1-17-loki001130c
+ includes the full ChangeLog (duh).
+
+ * CVS: up until cvs-1.17-001130, code/ui/ contained the
+ Q3 code for the UI QVM/DLL. In Q3TA, this code has been
+ moved to code/q3_ui/, while at the same time the new
+ (scripting driven) UI code for Q3TA was maintained in
+ code/ui/. To preserve the history of code/ui/, it has been
+ renamed to q3/ui/ in the CVSROOT.
+ Note: this will BREAK all cvs-1.17 and before checkouts.
+ To compile earlier versions, move or link q3_ui/ to ui/.
+ The code has been tagged
+ cvs tag pr1-17-loki001130
+ after the change.
+ DONE: remove code/*/vm/*.asm from CVSROOT
+ Note: this includes code/*/*.asm files (from *_syscalls.c).
+ These were originally tracked in CVS, but if we need
+ comparison of q3asm output or QVM files we can rely
+ on the Win32 and Linux SDK now. These files have been
+ physically removed from CVS now, followed by
+ cvs tag pr1-17-loki001130a
+
+2000-11-30 Bernd Kreimeier <bk@lokigames.com> *** MISSIONPACK ***
+
+ * RC2: new ZIP file (another 360M for convenience).
+
+ * SOS: new CVS module, Quake3_sos. This will be used to track
+ the unchanged SOS checkouts from id. As their repository
+ is read-only, and there is no estimate on when changes might
+ be backpropagated there, I will track their changes in a
+ separate module, and update our local Quake3 module
+ accordingly. This is effectively "tracking 3rd party"
+ w/o import and half-automated, forced mergers - in other
+ words, we now branch starting with our post-1.17 changes,
+ for the benefit of moving at all.
+ Baseline is a slightly changed PR-1.17 id000516 source dump
+ (essentially ui/ moved to q3_ui for continuity, and CR/LF etc.).
+ cvs import Quake3_sos id000516 pr1-17-win32
+ cvs import Quake3_sos sos001119 pr1-26-win32
+ cvs import Quake3_sos sos001120 pr1-26-win32-001120
+ cvs import Quake3_sos sos001121 pr1-26-win32-001121
+ cvs import Quake3_sos sos001122 pr1-26-win32-001122
+ cvs import Quake3_sos sos001122-demo pr1-26-win32-demo
+ This is about the 1.26w Team Arena Win32 demo release, give or
+ take a couple of lines. Has Q3_VERSION "Q3 Team Arena Demo 1.27b".
+ cvs import Quake3_sos sos001123 pr1-26-win32-001123
+ cvs import Quake3_sos sos001126 pr1-26-win32-001126
+ Now track id versions (see code/game/q_shared.h:Q3_VERSION)
+ cvs import Quake3_sos sos001128 pr1-27c-win32-001128
+ With 1.27d they switched from Demo to full version (RC1).
+ cvs import Quake3_sos sos001129 pr1-27d-win32-001129
+ cvs import Quake3_sos sos001130a pr1-27d-win32-001130a
+ Now switched to 1.27e. This import is done from the SOS
+ working directory.
+ cvs import Quake3_sos sos001130b pr1-27e-win32-001130b
+ Note: SoS created rwx attributes which are luckily fixed
+ automagically during import. It is also seemingly incapable
+ to compare files, and leave files that have not changed the
+ hell alone. I can't do cvs update due to the $..$ tags in
+ the original files (which CVS can't be told to ignore),
+ so I have to do import (creating a load of vendor tagged
+ branches), but at least cvsweb and cvs get the revisions
+ right.
+
+ * code-sos/unix/Makefile: added linux_joystick
+ * code-sos/unix/linux_local.h: match mac/ and win32/, for prototypes.
+
+ * code-sos/unix/linux_joystick.c: new file, code from linux_glimp.c
+ Note: decided to separate this, as (a) we might edit/extend
+ a lot, (b), it's not in the id tree, (c) it's not GL, (d)
+ there might be even more oddball devices. Anything that
+ cuts down on diffs.
+
+ * code-sos/unix/linux_glimp.c (Q_stristr): const return (cvs1.17).
+ Also (XLateKey): added more keyboard mappings (ASCII on
+ upper row digits) (cvs1.17). Added in the minimal joystick
+ hooks (cvars, function calls). Fixed joystick cvar naming
+ to match win32 (kept joystick_threshold).
+ TODO: joystick stubs for dedicated?
+
+ * CVS: I have to move up to 1.27d (data, Win32 networking).
+ With exception of linux_glimp.c (mostly joystick code),
+ all cvs1.17 changes should now be in the work snapshot
+ based on the first sos001119 we got from id. There are
+ also some additional changes in there already, thus I'll
+ move the (buggy) 1.26 snapshot into CVS before adding even
+ more differences.
+
+
+2000-11-29 Bernd Kreimeier <bk@lokigames.com> *** MISSIONPACK ***
+
+ * RC1: TeamArena_Q3A_RC1.zip. Source has moved from
+ Q3VERSION "Q3 Team Arena Demo 1.27c" to "Q3 1.27d" now.
+
+ * code-sos/qcommon/common.c: added Com_InitPushEvent(). Also
+ increased MAX_PUSHED_EVENTS to 256.
+ Note: this is another case of buffer memory not zero'ed.
+ Com_EventLoop, fixed evTime to evType in debug print.
+
+ * TEST: baseq3/
+ +set sv_pure 0 +set vm_game 0 +set in_mouse 0 +set developer 2
+ TODO: Team Arena in menu leads to RE_Shutdown(1) and locks
+ TODO: can't play game
+ TODO: shaders can't load *.tga, *.jpg files are there
+ TODO: DO_CC linking for DLL's, DO_SHLIB_CC only for export?
+ TODO: ERROR: Bad player movement angle
+ TODO: Warning: cvar "..." given initial values: "..." and "..."
+ TODO: TA demo ERROR: CL_ParseServerMessage: Illegible server message
+ TODO: WARNING: Com_PushEvent overflow
+
+ * code-sos/qcommon/files.c: add NULL filter for our Sys_ListFiles calls.
+ * unix/unix_shared.c (Sys_ListFiles): signature has changed,
+ additional Sys_ListFiles argument now.
+
+ * code-sos/unix/unix_net.c (Sys_GetPacket): see below (readcount=0).
+ * code-sos/unix/unix_main.c: see below (Mike's and my changes to DLL
+ loading, my event buffer clear fixes).
+ * code-sos/unix/linux_qgl.c (QGL_Init): see below (__FX__ guards).
+ TODO: abstract WGL/GLX and end unfortunate QGL duplication.
+ TODO: spice up QGL with Linux H2 full version.
+ * code-sos/q3_ui/ui_demo2.c: fix on demo names - no Q_strupr(demoname).
+ Note: in CVS this fix is in ui/ui_demo2.c. CVS is screwed by
+ id choosing the old name for new directory...
+ TODO: manual intervention on "ui goes q3_ui" in CVSROOT?
+ * renderer/qgl.h: see below (__FX__ guards).
+ * qcommon/files.c: migrated in the 1.17cvs changes against the
+ id000516 code dump, i.e. the (not marked - boo hiss) mkv changes.
+ Note: all the above is based on a diff of the last id code dump
+ pre-1.17 against our CVS, with those fixes now migrated into the
+ sos1.26 snapshot.
+ TODO: move in joystick code.
+ TODO: replace XAutoRepeatOn/Off with filter (focus).
+ TODO: DGA 2.0 and such.
+
+ * code-sos/game/q_shared.c: valid compare for NULL strings
+ * code-sos/unix/unix_main.c: QRTLD, and now using RTLD_NOW.
+ Note: it is a bad idea to load game DLL's that are missing symbols.
+
+ * code-sos/ui/ui_main.c: see below.
+ * code-sos/game/g_main.c: see below.
+ * code-sos/q3_ui/ui_main.c: see below.
+ * code-sos/cgame/cg_main.c: made cvarTable and cvarTableSize static. This resolved
+ a segfault related to traversing the UI table during Init.
+ Note: there is a segfault related to this variable being out of bounds.
+ Different struct size in global variables possible aliasing between the
+ DLL's.
+
+ * code-sos/unix/unix_main.c (Sys_Error): assert(0), no exit in debug.
+ * code-sos/game/q_shared.c: now aborts on NULL destination. Also DPrintf's
+ on bogus excess copies.
+ TODO: make all those string functions safe, at least assert.
+ * code-sos/server/sv_init.c: comment in SV_Init
+ // init the botlib here because we need the pre-compiler in the UI
+ Called in qcommon/common.c:Com_Init, were CL_Init is called afterwards...
+ * code-sos/server/sv_bot.c: the botlib_import is filled here.
+ * code-sos/unix/unix_main.c (Sys_GetBotLibAPI): RTLD_NOW. Which is for naught,
+ as this code is not used and has never been used. assert(0)
+
+ * code-sos/botlib/be_interface.c: botimport supposed to be set here.
+ * code-sos/botlib/l_memory.c: segfault with q3_ui/ DLL.
+ #1 0x80e23ec in GetMemory (size=35) at ..//botlib/l_memory.c:331
+ 331 ptr = botimport.GetMemory(size + sizeof(unsigned long int));
+ as botimport is completely NULL'ed.
+
+ * code-sos/q3_ui/q3_ui.sh: created from ui/ui.sh 1.17
+
+ * code-sos/q3_ui/q3_ui.q3asm: unfubared (CR/LF, / path).
+
+ * code-sos/unix/Makefile: added q3_ui/ make targets (basically
+ ui/ targets from CVS 1.17 Makefile for starters).
+
+ * code-sos/q3_ui/: this is the old UI code, which does not use
+ ../ui/menus.txt (see ui/ui_main.c). In other words,
+ the code in ui/ now has to be compiled or qvm'ed
+ for missionpack/, but to create the necessary DLL or
+ QVM modules for baseq3/ we need to use q3_ui/.
+
+
+2000-11-27 Bernd Kreimeier <bk@lokigames.com> *** MISSIONPACK ***
+
+ * code-sos/game/bg_lib.c: ld problem with a custom "tan(..)"
+ TODO: loooking forward to SIGFPE on this code base.
+
+ * code-sos/ui/ui_util.c: this file is empty.
+
+ * code-sos/ui/ui.sh: new files:
+ ui_shared.c
+ ui_util.c
+ Replaced by the /ui/*.menu files:
+ q3lcc: can't find `../ui_cdkey.c'
+ q3lcc: can't find `../ui_ingame.c'
+ etc.
+
+ * code-sos/cgame/cgame.q3asm: added cg_newdraw entry.
+ Also added ui_shared entry.
+ * cgame/cgame.sh: added cg_newdraw.c entry.
+ Also added ../ui/ui_shared.c entry.
+
+ * code-sos/cgame/cg_newdraw.c: renamed (was cg_newDraw.c mixed case).
+ Note: the infidels have taken over.
+
+ * cgame/cgame.sh: added -DMISSIONPACK.
+ Note: w/o, q3lcc complains
+ ../cg_event.c:204: undeclared identifier `cg_singlePlayerActive'
+ ../cg_event.c:204: left operand of . has incompatible type `int'
+ which indicates that this source does not compile w/o MISSIONPACK
+ anymore. The baseq3/pak4.pk3 file in the Q3TA snapshot archives
+ are dated
+ 284464 11-10-00 14:02 vm/cgame.qvm
+ 463940 11-14-00 14:47 vm/qagame.qvm
+ 271596 11-14-00 14:48 vm/ui.qvm
+ the code dump is from 11-19.
+ Note: Make does not abort on q3lcc complains
+
+ * code-sos/game/game.sh: also added ai_vcmd.c entry.
+
+ * code-sos/ui/ui.q3asm: fubared (below). In addition, this is
+ the only one to have a
+ -o "/tmp/quake3/missionpack/vm/ui"
+ line in it. Given that the other 2 QVM modules are
+ also dependend on -DMISSIONPACK, this seems a real mess.
+ For now using the same path as the other 3.
+ * code-sos/cgame/cgame.q3asm: below.
+ * code-sos/game/game.q3asm: fubared. Fixed CR/LF and \ in paths
+ again (read by q3asm called by game.sh called by make).
+ * unix/Makefile: updated fpor DLL/QVM.
+ Note: also shell scripts to use q3lcc not lcc.
+
+
+2000-11-27 Bernd Kreimeier <bk@lokigames.com>
+
+ * code/unix/Makefile: now expects a run/ directory
+ relative (between this, the Loki standards, and the
+ utility code in the same repository, it's ever so
+ slightly less dorky).
+ TODO: fix broken copyfiles target etc.pp.
+
+ * code/game/bg_lib.c: turns out the changes I
+ undid 001120 were affecting original Zoid
+ Linux port related defines, which break VM
+ compile. Mike fixed those (which I unfixed
+ when referring to the latest id code that does
+ not contain these patches). However, they
+ duplicate ANSI libc symbols, so the guards might
+ be wrong. The symbols are missing when compiling
+ for VM, so I now use the existing lcc -DQ3_VM
+ flag:
+ //#if !defined ( _MSC_VER ) && ! defined ( __linux__ )
+ #if defined ( Q3_VM )
+ This will break DLL compile on non-ANSI platforms,
+ which will have to be added to the conditional then.
+
+ * code/ui/ui.sh: below.
+ * code/game/game.sh: below.
+ * code/cgame/cgame.sh: Linux SDK installs q3lcc to
+ avoid collisions with regular lcc pre-installs. The
+ scripts fail with "lcc not found", but do not abort
+ the Makefile.
+ Note: now that VM code gets actually built, there
+ are errors:
+ g_main:648 ERROR: symbol vsprintf undefined
+ bg_pmove:1221 ERROR: symbol abs undefined
+ q_math:4309 ERROR: symbol fabs undefined
+ q_shared:2801 ERROR: symbol tolower undefined
+ q_shared:2862 ERROR: symbol toupper undefined
+ ai_dmq3:208 ERROR: symbol atoi undefined
+ ai_cmd:4951 ERROR: symbol sscanf undefined
+
+
+2000-11-20 Bernd Kreimeier <bk@lokigames.com>
+
+ * TEST: test compile of pr-1.17+cvs fixes segfaults due
+ to new baseq3/pak4.pk3
+ Note: to self ... 1.17 is not compatible with new files.
+ Checking into CVS next.
+
+ * code/: changes applied by us that are not in id's code base
+ affect q_shared.c (NULL in Q_stricmp), files.c (FIXME fs_cdpath,
+ Sys_ConcatenateFileList, ui_demo2.c (demo no tolower on linux).
+ In unix/ linux_glimp.c (joystick code), qgl.h, linux_qgl.c (__FX__),
+ unix_main.c (dlopen bug and event buffers), unix_net.c (readcount),
+ matha.s (assembly warning).
+
+ * code/server/sv_client.c (SV_WriteDownloadToClient):
+ No effective change on FS_SV_FOpenFileRead call, they reworked
+ autodownload some more seemingly.
+
+ * code/renderer/tr_surface.c: VectorArrayNormalize
+
+ * code/qcommon/qcommon.h: see below.
+ * code/qcommon/files.c: Com_ReadConfigs removed.
+ * code/qcommon/common.c: removed Com_ReadConfigs,
+ textual replacement of body in Com_Init.
+
+ * code/mac/mac_net.c: not applied (undone by id)
+ OTConfiguration *config <> OTConfigurationRef config
+
+ * code/mac/mac_glimp2.c: r_colorbits->integer > 16
+ * code/game/surfaceflags.h (CONTENTS_BOTCLIP): added.
+
+ * code/game/q_shared.h: not applied (undone by id)
+ #if defined(ppc) || defined(__ppc) || defined(__ppc__)
+ #define idppc 1
+ #else
+ #define idppc 0
+ #endif
+
+ * code/game/q_math.c: added another CPP line to guard
+ BoxOnPlaneSide, removed WIN32 guard.
+ TODO: this could be broken code guarded in all current
+ compiles...
+
+ * code/game/bg_lib.c: left Q#_VM guard for typedef cmp_t
+ Added !defined( __linux__ ) for tolower and atoi.
+ Note: the changes above relate to the very last code update
+ from id prior to the 6 month blackout, which were not in
+ CVS when Michael made his updates. Needed to establish the
+ baseline for the new patch. Source dump 1.17.00520, against
+ SOS 1.26w-001119 version.
+
+2000-11-20 Bernd Kreimeier <bk@lokigames.com> *** MISSIONPACK ***
+
+ * TEST: running against the data up to TeamArena_Q3A_001109.zip
+ Hunk_Clear: reset the hunk ok
+ Program received signal SIGBUS, Bus error.
+ "q3dm2", killBots==qtrue
+ #0 CM_ClearMap () at ..//qcommon/cm_load.c:644
+ #1 0x80884a7 in SV_Map_f () at ..//server/sv_ccmds.c:159
+ #2 0x8072579 in Cmd_ExecuteString (text=0xbffff4b0 "spmap q3dm2") at ..//qcommon/cmd.c:591
+ #3 0x8071dfe in Cbuf_Execute () at ..//qcommon/cmd.c:190
+ #4 0x80763f7 in Com_Frame () at ..//qcommon/common.c:2547
+ #5 0x8130d6b in main (argc=13, argv=0xbffff984) at ..//unix/unix_main.c:953
+ #6 0x40100cb3 in __libc_start_main (main=0x8130bc4 <main>
+ Not reproducible (screen stayed black).
+
+ * TEST: +set developer 1, same for Win32 and Linux:
+ Can't find gfx/misc/flare.tga
+ Can't find gfx/misc/sun.tga
+ Can't find gfx/misc/console02.tga
+ Can't find vm/ui.map
+ Can't find textures/sfx/logo512.tga
+ Can't find gfx/colors/black.tga
+ Can't find models/mapobjects/banner/banner5_2.md3
+ Can't find models/mapobjects/banner/banner5_1.md3
+ Can't find textures/sfx/firegorre2.tga
+ Can't find textures/sfx/bolts.tga
+ Can't find menu/art/unknownmap.tga
+
+ * Q3TA: after nearly 6 months, a code update from id. SOS access
+ even. Got it to compile, link and start, but its currently broken
+ (menu doesn't render in full, can't get into game etc.). Need
+ a baseline 1.17 to diff against. Last code dump was May 16, with
+ bspc code updated May 19. Checking working directory of bk000520
+ against CVS next (Mike's fixes never made it into id's codebase
+ or a post 1.17 release, neither did my fixes as released in the
+ point release version 1.17).
+
+2000-11-19 Bernd Kreimeier <bk@lokigames.com> *** MISSIONPACK ***
+
+ * TEST: Win32 install as tested with 1.26w. quake3.x86 (Q3A game)
+ Warning: cvar "r_uifullscreen" given initial values: "1" and "0"
+ Warning: cvar "r_inGameVideo" given initial values: "1" and "0"
+ ^3WARNING: sound/feedback/hit.wav is a 8 bit wav file
+ (on windows, sound/weapons/weapon_hover.wav is missing...)
+ Menu only partially displayed in TA and baseq3 play, menu itself
+ seems to work. Freetype?
+ WARNING: Com_PushEvent overflow
+
+ * code-sos/game/game.sh: not in SOS, moved in from CVS snapshot.
+
+ * code-sos/qcommon/common.c: conditional DEDICATED to get rid off
+ CL_ShutdownCGame/CL_ShutdownUI/CIN_CloseAllVideos.
+ Same for UI_usesUniqueCDKey: dedicated server does not
+ write CD key file.
+ TODO: check whether there is an unneeded "read CD key"
+ for dedicated server.
+
+ * code-sos/null/null_client.c (CL_ShutdownAll): added dummy.
+
+ * code-sos/unix/Makefile: server/sv_net_chan.o for dedicated server.
+
+ * code-sos/null/null_snddma.c: fixed S_RegisterSound signature.
+
+ * code-sos/client/snd_mix.c: snd_p, snd_linear_count, snd_out
+ can't be static, as used by unix/snd_mixa.s.
+
+ * code-sos/unix/Makefile: added to the executable target:
+ renderer/tr_font.c
+ client/cl_net_chan.c
+ server/sv_net_chan.c
+ Also added a lot of jc*.c files to build, to fix unresolved
+ symbol errors.
+ TODO: is there unused jpeg-6/jd*.o code linked in now?
+
+ * code-sos/ft2/smooth.c: includes ftgrays.c, ftsmooth.c
+
+ * code-sos/ft2/truetype.c: ttdriver.c, ttpload.c, ttgload.c, ttobjs.c.
+ Also (see ftoption.h) TT_CONFIG_OPTION_BYTECODE_INTERPRETER ttinterp.c
+
+ * code-sos/ft2/sfnt.c: includes ttload.c, ttcmap.c, sfobjs.c,
+ sfdriver.c. lso (see ftoption.h)
+ TT_CONFIG_OPTION_EMBEDDED_BITMAPS ttsbit.c
+ TT_CONFIG_OPTION_POSTSCRIPT_NAMES ttpost.c
+
+ * code-sos/ft2/ftbase.c: includes ftcalc.c, ftobjs.c, ftstream.c,
+ ftlist.c, ftoutln.c, ftextend.c, ftnames.c.
+
+ * code-sos/ft2/autohint.c: includes ahangles.c, ahglyph.c, ahglobal.c,
+ ahhint.c, ahmodule.c.
+
+ * code-sos/unix/Makefile: added ft2/ to client objects, took out
+ ftraster.c/ftrend1.c (see below), added -DFT_FLAT_COMPILE.
+ * ft2/ftsmooth.c: -DFT_FLAT_COMPILE required.
+ * ft2/raster1.c: -DFT_FLAT_COMPILE required.
+ Note: this includes ftraster.c/ftrend1.c.
+
+ * code-sos/qcommon/vm_x86.c: _ftol is missing, ftolPtr only defined
+ for Win32, but used in generic code. Workaround for now.
+ TODO: find good Linux ftol, or use old solution.
+
+ * SoS checkout. chown -R a+w * recode ibmpc:lat1 */*.h */*.c
+
+2000-06-30 Michael Vance <briraeos@lokigames.com>
+
+ * misc: Spoke with Leonardo about qvm mess.
+
+ * ui/ui.sh: Created to build much like the ui.bat script.
+
+ * ui/ui.q3asm: Use linux style paths.
+
+ * game/game.sh: Created to build much like the game.bat script.
+
+ * game/game.q3asm: Use linux style paths.
+
+ * cgame/cgame.sh: Created to build much like the cgame.bat script.
+
+ * cgame/cgame.q3asm: Use linux systel paths.
+
+ * unix/Makefile: Use the new .sh scripts to build the QVM files.
+
+ * lcc/etc/linux.c: Build .asm files instead of .s files.
+
+ * misc: QVMs now load properly, with minor glitches that should
+ hopefully be solvable. The new build scripts conflict with the
+ .asm files already in CVS, as the generated byte code is slightly
+ different in some cases.
+
+2000-06-29 Michael Vance <briareos@lokigames.com>
+
+ * lcc/makefile: Tweaked to automatically include the system
+ compiler's header location. Added an install directory.
+
+ * lcc/custom.mk: Added a build directory.
+
+ * lcc/etc/linux.c: Numerous small tweaks to make compiling the VM
+ code a much simpler task.
+
+ * q3asm/Makefile: Created.
+
+ * q3asm/q3asm.c: Fixed uninitialized variable in
+ HashString(). Fixed off by one in argument parsing.
+
+ * misc: Had Brian remove the Xmd.h include from glx.h so that we can
+ build Quake3 on XFree86 4.0 systems.
+
+ * wine: Attempted to build with lcc.exe and q3asm.exe using wine,
+ also did not work. This is in contrast to MikeP's .qvms, which
+ seem to work.
+
+2000-06-28 Michael Vance <briareos@lokigames.com>
+
+ * common/files.c: Fixed Mods menu behaviour.
+
+ * unix/linux_qgl.c: Guarded references to fxMesa.
+
+ * renderer/qgl.h: Guarded references to fxMesa.
+
+ * ui/ui_demo2.c: Don't convert filename to uppercase.
+
+2000-05-07 Bernd Kreimeier <bk@lokigames.com>
+
+ * common/cmdlib.c: windowism, not guarded. Added WIN32 around "ATOM a".
+
+ * q3map/Makefile: Linux Makefile.
+
+ * q3map/Makefile.irix: "makefile" in original code, Irix-only Makefile.
+ Just fixed some redundant TAB that GNU make despises about as much as I
+ despise GNU Make, and changed to a relative path.
+
+2000-05-01 Bernd Kreimeier <bk@lokigames.com>
+
+ * q3radiant/: updated with Q3Radiant198b3-src.zip.
+ Tagged (globally) as q3radiant-198b3.
+ Kept the old files
+ 3DFXCamWnd.h
+ 3DFXCamWnd.cpp
+ MainFrm2.cpp
+ New files
+ Shaders.h
+ misc/ (contributed special TGA resources, don't relly belong)
+ Removed:
+ pName
+ Changed filenames to previous case:
+ UNNAMED.MAP -> unnamed.map
+ RES/BMP0002.BMP -> RES/bmp00002.bmp
+ Changed:
+ changelog.txt -> ChangeLog
+
+2000-04-28 Bernd Kreimeier <bk@lokigames.com>
+
+ * CVS: bk000425 modified sources. This replaces the unix/ directory
+ which is not yet in id's SourceSafe. Two check-ins, due to minor
+ changes in an attempt to nail the Voodoo3 related crashes (driver
+ problems, not a Q3 issue). Undid some of the QFL changes for PI
+ and the log bug fix - put back in (TODO). Also includes:
+ * Quake3/code/botlib/be_aas_sample.c: single file update from Robert.
+
+ * CVS: id000423 code dumps (two of them). Applying Loki patches.
+ Tagged for the final version (all patches).
+
+ * CVS: id000422 code dump. This did not include the 1.16n fixes
+ used for Linux, and was the first dump for the 1.17 security fix
+ release.
+ Note: forgot to check in the ft2/ headers themselves, but they
+ are not used in the current codebase anyway. Are added in next
+ dump. Also there is use of CVS/CVS-like $Keyword$ patterns in
+ some files, and between their revisions and ours we fuck this up.
+ Also, id ZIP files create write protected sources, have to do
+ chmod -R a+w Quake3/ to work and overwrite files.
+
+ * CVS: bk000315 modified source. This version was the 1.16n release.
+ Note: the changes applied here are not in the subsequent code dumps
+ of id. If you want to compile the Linux version as released you
+ have to use bk-tagged versions until the patches are merged in by
+ Robert Duffy.
+
+ * CVS: id000314 engine code dump, same procedure as below, tag.
+ Note: this version added vm/ sudirectories with assembly files
+ for cgame, game, ui. CVS tag id000314.
+
+ * CVS: id000304 engine code dump. Now there is a problem, as CVS
+ was used in the Mac sources. Do
+ find . -name 'CVS' -exec rm -r {} \;
+ before cvs update, then tagged:
+ cvs -d /loki/cvsroot/ tag id000304 Quake3/
+
+ * CVS: checked in a source snapshot of the id00303 engine code
+ and the id0003029 tools code. The tool sources are not fully in
+ sync, and we have only partial source from earlier engine revisions.
+ The engine source marks where Loki took over from Dave Kirsch.
+ This snapshot (with all temporary and bogus files) is imported
+ and tagged using:
+ cvs -d /loki/cvsroot import Quake3 id000303 initial
+
+ Modules:
+ code: the Q3 engine code, including a jpeg-6/ copy
+ common: code shared by tools
+ libs: code shared by tools, inlcuding a jpeg6/ copy
+ q3asm: VM bytecode assembly
+ q3data: misc. Q3 data conversions
+ q3map: BSP builder
+ q3radiant: Win32 editor, as is
+ lcc: C compiler for q3asm
+
+ The sources have not been cleaned up, and binary files have not been
+ removed. The Q3Radiant code base might exhibit mixed case asmbiguities
+ in the future, and future source dumps might come from SourceForge
+ instead.
+
+2000-04-25 Bernd Kreimeier <bk@lokigames.com>
+
+ * q3code.id000425/unix/Makefile: relative path, relocatable.
+ Note: first code merge with id, finally :-).
+
+2000-04-24 Bernd Kreimeier <bk@lokigames.com>
+
+ * q3code.bk000422/unix/matha.s: in C(BoxOnPlaneSide)
+ the following line triggers assembler warning:
+ "missing prefix `*' in absolute indirect address, maybe misassembled!"
+ jmp Ljmptab(,%eax,4)
+
+
+ * q3code.bk000422/unix/Makefile (MOUNT_DIR): rember to change.
+ TODO: fix this bloody Makefile to be relocatable, damnit.
+
+ * q3code.bk000422/cgame/cg_event.c: applied JCash fix again
+ (see EV_EVENT_BITS below). Send e-mail to verify.
+
+ * q3code.bk000422/renderer/tr_image.c: "../jpeg-6/jpeglib.h" again.
+
+ * q3code.bk000422/: created from the id dump of today, lacking
+ all but one of my changes (sigh). Swapped unix/ competely, takes
+ care of 90%. Submitted all changes again to Robert...
+
+2000-04-19 Bernd Kreimeier <bk@lokigames.com>
+
+ * q3code.bk000315/unix/linux_glimp.c (GLimp_EndFrame):
+ QGL_EnableLogging( r_logFile->value ) doesn't work?
+
+ * q3code.bk000315/unix/linux_qgl.c: GLimp_LogNewFrame() is
+ obsolete. QGL_EnableLogging was out of sync with Win32 and
+ did not support the new framecounter decrement logic.
+
+2000-04-03 Bernd Kreimeier <bk@lokigames.com>
+
+ * q3code.bk000315/server/sv_snapshot.c: svs.nextSnapshotEntities
+ is a signed integer unconditionally incremented, which gets
+ negative and causes a segfaulting indexing an array. Added reset
+ to counter. Might fail if snapshot numbers are supposed to
+ monotonically increase.
+
+2000-04-02 Bernd Kreimeier <bk@lokigames.com>
+
+ * q3code.bk000315/client/cl_parse.c (CL_ParseServerMessage):
+ assert(0) on Illegible message (remember to +set in_mouse 0).
+ TODO: have to add a dump message function, it's unreadable.
+
+ * botlib/be_ai_goal.c (InitLevelItemHeap): loop counter -2
+ left -2 with uninitialized next, and -1 disconnected. Removed
+ redundant memset. There is an item alloc leak I suspect, as
+ max_levelitems 1024 merely delayed the overflow error.
+
+2000-04-01 Bernd Kreimeier <bk@lokigames.com>
+
+ * botlib/be_ai_goal.c (InitLevelItemHeap): still segfaults.
+ Not memsetting the entire item heap. As items are cleared
+ on return, that leaves only memory corruption?
+ Later: upped max_levelitems from 256 to 1024
+ Later: client dies on connect:
+ Error: CL_ParseServerMessage: Illegible server message 255
+
+
+2000-03-31 Bernd Kreimeier <bk@lokigames.com>
+
+ * botlib/be_ai_goal.c: initializing global vars.
+ Segfault in AllocLevelItem ()
+ at /home/bk/Games/Quake3/q3code/botlib/be_ai_goal.c:364
+ I suspect that the initial freelevelitems setting is at
+ the end of the list and eventually exposed.
+
+ * cgame/cg_event.c: according to Johmn Cash:
+ itemNum = (es->event & ~EV_EVENT_BITS) - EV_USE_ITEM0
+ Quote: "This causes itemNum to be invalid about half the time,
+ preventing any client side effect tied to the item from occurring."
+
+2000-03-06 Bernd Kreimeier <bk@lokigames.com>
+
+ * qcommon/common.c: set pushEvent buffer and indices
+ to zero in Com_Init().
+
+ * q3code/qcommon/qcommon.h: made SE_NONE (and for paranoia
+ also NA_BOT) explicitely set to zero.
+
+2000-02-27 Bernd Kreimeier <bk@lokigames.com>
+
+ * unix/Makefile: added dmalloc in an attempt to get on
+ the Z_Free bug. Futile. Despite stripping dmalloc debug
+ token down to essentials, I get a (seemingly bogus or
+ unrelated):
+ debug-malloc library: dumping program, fatal error
+ Error: possibly bad .c filename pointer (err 24)
+
+
+2000-02-26 Bernd Kreimeier <bk@lokigames.com>
+
+ * qcommon/common.c: various debug builts to isolate the
+ Z_Free bug. It reproducibly happens on some machines
+ with SE_PACKET, but the packets themselves look
+ thoroughly corrupted.
+
+2000-02-21 Bernd Kreimeier <bk@lokigames.com>
+
+ * qcommon/common.c (Com_EventLoop): possible problem
+ here, pointer does not get cleared.
+
+ * unix/linux_glimp.c (InitSig): no signal handler.
+ * common/common.c: dump in Com_Error for debug.
+
+2000-02-17 Bernd Kreimeier <bk@lokigames.com>
+
+ * q3code: new dump from Zoid. Repeat tr_image.c fix.
+
+ * unix/Makefile: added client/snd_adpcm.c (linkage errors).
+ Later: added entire JPDIR and rules, for tr_image.c.
+ Later: had to fix fules for game/ai_*.c files.
+ Later: removed ui/ui_quit.o (n/a)
+ Later: took out -mpentiumpro -march=pentiumpro
+
+ * renderer/tr_image.c: windowism in #include path (see below).
+ #include "..\jpeg-6\jpeglib.h"
+
+1999-12-27 Bernd Kreimeier <bk@lokigames.com>
+
+ * Alpha: tried a dedicated server compile. Segfaults in
+ ../qcommon/files.c:1682, a paksort function doing pointer
+ fiddling.
+
+ * Makefile.alpha: created.
+ Note: want to take the SDL/Setup autoconf ASAP.
+
+ * unix/unix_main.c: fixed __axp__ to __alpha__, guarded
+ _FPU_SETCW.
+
+ * qcommon/vm_alpha.c: dummy, created.
+ * qcommon/vm_null.c: dummy, created.
+
+1999-12-04 Bernd Kreimeier <bk@lokigames.com>
+
+ * renderer/tr_image.c: windowism in #include path.
+ #include "..\jpeg-6\jpeglib.h"
+
+ * Revision 1.11: from Zoid by e-mail.
+ Note: threw away my playground copy, starting with the
+ ZIP file. Zoid's using CVS now, but we can't remote
+ access it. Thus did the
+ "find . -name 'CVS' -exec rm -rf {} \;"
+ and then track it as 3rd party source by
+
+
+ * ChangeLog: created. Now starting to track Q3A source.
+
+--------- q3code log ---------------------------------------------
diff --git a/code/unix/Cons_gcc.pm b/code/unix/Cons_gcc.pm
new file mode 100755
index 0000000..1b24589
--- /dev/null
+++ b/code/unix/Cons_gcc.pm
@@ -0,0 +1,47 @@
+#
+# Some utilities to handle gcc compiler setup
+#
+
+package Cons_gcc;
+
+# pass the compiler name
+# returns an array, first element is 2 for 2.x 3 for 3.x, then full version, then machine info
+sub get_gcc_version
+{
+ my @ret;
+ my ($CC) = @_;
+ my $version=`$CC --version | head -1`;
+ chop($version);
+ my $machine=`$CC -dumpmachine`;
+ chop($machine);
+ if($version =~ '2\.[0-9]*\.[0-9]*')
+ {
+ push @ret, '2';
+ } else {
+ push @ret, '3';
+ }
+ push @ret, $version;
+ push @ret, $machine;
+ return @ret;
+}
+
+# http://ccache.samba.org/
+# check ccache existence and path
+# returns an array, first element 0 / 1, then path
+sub get_ccache
+{
+ my @ret;
+ $ccache_path=`which ccache`;
+ chop($ccache_path);
+ if(-x $ccache_path)
+ {
+ push @ret, '1';
+ push @ret, $ccache_path;
+ return @ret;
+ }
+ push @ret, '0';
+ return @ret;
+}
+
+# close package
+1;
diff --git a/code/unix/Conscript-client b/code/unix/Conscript-client
new file mode 100755
index 0000000..dc2e8b8
--- /dev/null
+++ b/code/unix/Conscript-client
@@ -0,0 +1,265 @@
+# full client build script
+
+Import qw( BASE_CFLAGS BASE_LDFLAGS INSTALL_DIR BUILD_DIR TARGETNAME CC CXX LINK );
+
+# splines
+$env_splines = new cons(
+ CC => $CC,
+ CXX => $CXX,
+ LINK => $LINK,
+ ENV => { PATH => $ENV{PATH}, HOME => $ENV{HOME} }
+ );
+
+@SPLINES_FILES = qw(
+ ../splines/math_angles.cpp
+ ../splines/math_matrix.cpp
+ ../splines/math_quaternion.cpp
+ ../splines/math_vector.cpp
+ ../splines/q_parse.cpp
+ ../splines/q_shared.cpp
+ ../splines/splines.cpp
+ ../splines/util_str.cpp
+ );
+$SPLINES_FILESREF = \@SPLINES_FILES;
+
+Library $env_splines 'splines', @$SPLINES_FILESREF;
+
+# botlib
+# FIXME TTimo
+# we already deal with botlib on dedicated target
+# the compilation options are a bit different but we could certainly rely on
+# the same Conscript and make things cleaner
+
+$env_botlib = new cons(
+ CC => $CC,
+ CXX => $CXX,
+ LINK => $LINK,
+ ENV => { PATH => $ENV{PATH}, HOME => $ENV{HOME} },
+ CFLAGS => $BASE_CFLAGS . '-DBOTLIB '
+);
+
+@BOTLIB_FILES = qw(
+ ../botlib/be_aas_bspq3.c
+ ../botlib/be_aas_cluster.c
+ ../botlib/be_aas_debug.c
+ ../botlib/be_aas_entity.c
+ ../botlib/be_aas_file.c
+ ../botlib/be_aas_main.c
+ ../botlib/be_aas_move.c
+ ../botlib/be_aas_optimize.c
+ ../botlib/be_aas_reach.c
+ ../botlib/be_aas_route.c
+ ../botlib/be_aas_routealt.c
+ ../botlib/be_aas_sample.c
+ ../botlib/be_ai_char.c
+ ../botlib/be_ai_chat.c
+ ../botlib/be_ai_gen.c
+ ../botlib/be_ai_goal.c
+ ../botlib/be_ai_move.c
+ ../botlib/be_ai_weap.c
+ ../botlib/be_ai_weight.c
+ ../botlib/be_ea.c
+ ../botlib/be_interface.c
+ ../botlib/l_crc.c
+ ../botlib/l_libvar.c
+ ../botlib/l_log.c
+ ../botlib/l_memory.c
+ ../botlib/l_precomp.c
+ ../botlib/l_script.c
+ ../botlib/l_struct.c
+);
+$BOTLIB_REF = \@BOTLIB_FILES;
+
+Library $env_botlib 'botlib', @$BOTLIB_REF;
+
+# jpeg
+# NOTE TTimo we might need this one on other targets
+$env_jpeglib = new cons(
+ CC => $CC,
+ CXX => $CXX,
+ LINK => $LINK,
+ ENV => { PATH => $ENV{PATH}, HOME => $ENV{HOME} },
+ CFLAGS => $BASE_CFLAGS
+ );
+
+@JPEGLIB_FILES = qw(
+ ../jpeg-6/jcapimin.c
+ ../jpeg-6/jchuff.c
+ ../jpeg-6/jcinit.c
+ ../jpeg-6/jccoefct.c
+ ../jpeg-6/jccolor.c
+ ../jpeg-6/jfdctflt.c
+ ../jpeg-6/jcdctmgr.c
+ ../jpeg-6/jcphuff.c
+ ../jpeg-6/jcmainct.c
+ ../jpeg-6/jcmarker.c
+ ../jpeg-6/jcmaster.c
+ ../jpeg-6/jcomapi.c
+ ../jpeg-6/jcparam.c
+ ../jpeg-6/jcprepct.c
+ ../jpeg-6/jcsample.c
+ ../jpeg-6/jdapimin.c
+ ../jpeg-6/jdapistd.c
+ ../jpeg-6/jdatasrc.c
+ ../jpeg-6/jdcoefct.c
+ ../jpeg-6/jdcolor.c
+ ../jpeg-6/jddctmgr.c
+ ../jpeg-6/jdhuff.c
+ ../jpeg-6/jdinput.c
+ ../jpeg-6/jdmainct.c
+ ../jpeg-6/jdmarker.c
+ ../jpeg-6/jdmaster.c
+ ../jpeg-6/jdpostct.c
+ ../jpeg-6/jdsample.c
+ ../jpeg-6/jdtrans.c
+ ../jpeg-6/jerror.c
+ ../jpeg-6/jidctflt.c
+ ../jpeg-6/jmemmgr.c
+ ../jpeg-6/jmemnobs.c
+ ../jpeg-6/jutils.c
+ );
+$JPEGLIB_REF = \@JPEGLIB_FILES;
+
+Library $env_jpeglib 'jpeglib', @$JPEGLIB_REF;
+
+# NOTE TTimo this requires patched cons version to work (see unix/cons)
+%nasm_hash = new cons()->copy(
+ CC => 'nasm',
+ CCCOM => '%CC -f elf -o %> %<'
+);
+$nasm_hash{SUFMAP}{'.nasm'} = 'build::command::cc';
+$nasm_env = new cons(%nasm_hash);
+
+Library $nasm_env 'asmlib', 'ftol.nasm', 'snapvector.nasm';
+
+# compiling files with inlined assembly
+$env_inlined = new cons(
+ CFLAGS => '-DELF -x assembler-with-cpp'
+ );
+
+Library $env_inlined 'inlinelib', '../unix/matha.s', '../unix/snd_mixa.s';
+
+# putting it all together
+
+$env = new cons(
+ CC => $CC,
+ CXX => $CXX,
+ LINK => $LINK,
+ ENV => { PATH => $ENV{PATH}, HOME => $ENV{HOME} },
+ CFLAGS => $BASE_CFLAGS,
+ LIBS => ' ' . $BUILD_DIR . '/unix/splines.a '
+ . $BUILD_DIR . '/unix/botlib.a '
+ . $BUILD_DIR . '/unix/jpeglib.a '
+ . $BUILD_DIR . '/unix/asmlib.a '
+ . $BUILD_DIR . '/unix/inlinelib.a '
+ . $BASE_LDFLAGS
+ . '-L/usr/X11R6/lib -lX11 -lXext -lXxf86dga -lXxf86vm -ldl -lm'
+);
+
+@RENDERER_FILES = qw(
+ ../renderer/tr_animation.c
+ ../renderer/tr_backend.c
+ ../renderer/tr_bsp.c
+ ../renderer/tr_cmds.c
+ ../renderer/tr_curve.c
+ ../renderer/tr_flares.c
+ ../renderer/tr_font.c
+ ../renderer/tr_image.c
+ ../renderer/tr_init.c
+ ../renderer/tr_light.c
+ ../renderer/tr_main.c
+ ../renderer/tr_marks.c
+ ../renderer/tr_mesh.c
+ ../renderer/tr_model.c
+ ../renderer/tr_noise.c
+ ../renderer/tr_scene.c
+ ../renderer/tr_shade.c
+ ../renderer/tr_shade_calc.c
+ ../renderer/tr_shader.c
+ ../renderer/tr_shadows.c
+ ../renderer/tr_sky.c
+ ../renderer/tr_surface.c
+ ../renderer/tr_world.c
+ );
+$RENDERER_REF = \@RENDERER_FILES;
+
+@CLIENT_FILES = qw(
+ ../client/cl_cgame.c
+ ../client/cl_cin.c
+ ../client/cl_console.c
+ ../client/cl_input.c
+ ../client/cl_keys.c
+ ../client/cl_main.c
+ ../client/cl_net_chan.c
+ ../client/cl_parse.c
+ ../client/cl_scrn.c
+ ../client/cl_ui.c
+ );
+$CLIENT_REF = \@CLIENT_FILES;
+
+@COMMON_FILES = qw(
+ ../qcommon/cm_load.c
+ ../qcommon/cm_patch.c
+ ../qcommon/cm_polylib.c
+ ../qcommon/cm_test.c
+ ../qcommon/cm_trace.c
+ ../qcommon/cmd.c
+ ../qcommon/common.c
+ ../qcommon/cvar.c
+ ../qcommon/files.c
+ ../qcommon/md4.c
+ ../qcommon/msg.c
+ ../qcommon/net_chan.c
+ ../qcommon/huffman.c
+ ../qcommon/unzip.c
+ );
+$COMMON_REF = \@COMMON_FILES;
+
+@SOUND_FILES = qw(
+ ../client/snd_adpcm.c
+ ../client/snd_dma.c
+ ../client/snd_mem.c
+ ../client/snd_mix.c
+ ../client/snd_wavelet.c
+ );
+$SOUND_REF = \@SOUND_FILES;
+
+@UNIX_FILES = qw(
+ ../unix/unix_main.c
+ ../unix/unix_net.c
+ ../unix/unix_shared.c
+ ../unix/linux_common.c
+ ../unix/linux_qgl.c
+ ../unix/linux_glimp.c
+ ../unix/linux_joystick.c
+ ../unix/linux_snd.c
+ ../unix/linux_signals.c
+ );
+$UNIX_REF = \@UNIX_FILES;
+
+@SERVER_FILES = qw(
+ ../server/sv_bot.c
+ ../server/sv_ccmds.c
+ ../server/sv_client.c
+ ../server/sv_game.c
+ ../server/sv_init.c
+ ../server/sv_main.c
+ ../server/sv_net_chan.c
+ ../server/sv_snapshot.c
+ ../server/sv_world.c
+ );
+$SERVER_REF = \@SERVER_FILES;
+
+# FIXME TTimo vm_<cpu>.c
+@VM_FILES = qw(
+ ../qcommon/vm.c
+ ../qcommon/vm_x86.c
+ ../qcommon/vm_interpreted.c
+ );
+$VM_REF = \@VM_FILES;
+
+# FIXME: import the CPU string to build the name of the target
+Program $env $TARGETNAME, '../game/q_shared.c', '../game/q_math.c',
+ @$RENDERER_REF, @$CLIENT_REF, @$COMMON_REF, @$SOUND_REF,
+ @$UNIX_REF, @$SERVER_REF, @$VM_REF;
+Install $env $INSTALL_DIR, $TARGETNAME;
diff --git a/code/unix/Conscript-dedicated b/code/unix/Conscript-dedicated
new file mode 100755
index 0000000..1401760
--- /dev/null
+++ b/code/unix/Conscript-dedicated
@@ -0,0 +1,115 @@
+# dedicated server build script
+
+Import qw( BASE_CFLAGS BUILD_DIR INSTALL_DIR CC CXX LINK );
+
+$DEDICATED_NAME = 'linuxq3ded';
+
+$env_botlib = new cons(
+ CC => $CC,
+ CXX => $CXX,
+ LINK => $LINK,
+ ENV => { PATH => $ENV{PATH}, HOME => $ENV{HOME} },
+ CFLAGS => $BASE_CFLAGS . '-DBOTLIB '
+);
+
+@BOTLIB_FILES = qw(
+ ../botlib/be_aas_bspq3.c
+ ../botlib/be_aas_cluster.c
+ ../botlib/be_aas_debug.c
+ ../botlib/be_aas_entity.c
+ ../botlib/be_aas_file.c
+ ../botlib/be_aas_main.c
+ ../botlib/be_aas_move.c
+ ../botlib/be_aas_optimize.c
+ ../botlib/be_aas_reach.c
+ ../botlib/be_aas_route.c
+ ../botlib/be_aas_routealt.c
+ ../botlib/be_aas_sample.c
+ ../botlib/be_ai_char.c
+ ../botlib/be_ai_chat.c
+ ../botlib/be_ai_gen.c
+ ../botlib/be_ai_goal.c
+ ../botlib/be_ai_move.c
+ ../botlib/be_ai_weap.c
+ ../botlib/be_ai_weight.c
+ ../botlib/be_ea.c
+ ../botlib/be_interface.c
+ ../botlib/l_crc.c
+ ../botlib/l_libvar.c
+ ../botlib/l_log.c
+ ../botlib/l_memory.c
+ ../botlib/l_precomp.c
+ ../botlib/l_script.c
+ ../botlib/l_struct.c
+);
+$BOTLIB_REF = \@BOTLIB_FILES;
+
+Library $env_botlib 'botlib', @$BOTLIB_REF;
+
+# NOTE TTimo this requires patched cons version to work (see unix/cons)
+%nasm_hash = new cons()->copy(
+ CC => 'nasm',
+ CCCOM => '%CC -f elf -o %> %<'
+);
+$nasm_hash{SUFMAP}{'.nasm'} = 'build::command::cc';
+$nasm_env = new cons(%nasm_hash);
+
+Library $nasm_env 'asmlib', 'ftol.nasm', 'snapvector.nasm';
+
+$env = new cons(
+ CC => $CC,
+ CXX => $CXX,
+ LINK => $LINK,
+ ENV => { PATH => $ENV{PATH}, HOME => $ENV{HOME} },
+ # FIXME TTimo I'm not sure about what C_ONLY is for
+ CFLAGS => $BASE_CFLAGS . '-DC_ONLY',
+ LDFLAGS => '-ldl -lm',
+ LIBS => ' '
+ . $BUILD_DIR . '/unix/botlib.a '
+ . $BUILD_DIR . '/unix/asmlib.a '
+);
+
+# list the files for the dedicated server
+@FILES = qw(
+ ../null/null_client.c
+ ../null/null_input.c
+ ../null/null_snddma.c
+ ../server/sv_bot.c
+ ../server/sv_ccmds.c
+ ../server/sv_client.c
+ ../server/sv_game.c
+ ../server/sv_init.c
+ ../server/sv_main.c
+ ../server/sv_net_chan.c
+ ../server/sv_snapshot.c
+ ../server/sv_world.c
+ ../qcommon/cm_load.c
+ ../qcommon/cm_patch.c
+ ../qcommon/cm_polylib.c
+ ../qcommon/cm_test.c
+ ../qcommon/cm_trace.c
+ ../qcommon/cmd.c
+ ../qcommon/common.c
+ ../qcommon/cvar.c
+ ../qcommon/files.c
+ ../qcommon/huffman.c
+ ../qcommon/md4.c
+ ../qcommon/msg.c
+ ../qcommon/net_chan.c
+ ../qcommon/unzip.c
+ ../qcommon/vm.c
+ ../qcommon/vm_interpreted.c
+ ../game/q_math.c
+ ../game/q_shared.c
+ ../unix/linux_common.c
+ ../unix/unix_main.c
+ ../unix/unix_net.c
+ ../unix/unix_shared.c
+ ../unix/linux_signals.c
+ );
+$FILESREF = \@FILES;
+
+# DEDICATED_NAME is imported, holds the name of the target
+# wolfded.x86 usually
+Program $env $DEDICATED_NAME, '../qcommon/vm_x86.c', @$FILESREF;
+Install $env $INSTALL_DIR, $DEDICATED_NAME;
diff --git a/code/unix/Conscript-pk3 b/code/unix/Conscript-pk3
new file mode 100755
index 0000000..9dcf691
--- /dev/null
+++ b/code/unix/Conscript-pk3
@@ -0,0 +1,135 @@
+# build pk3 on the fly
+
+Import qw( INSTALL_DIR BUILD_DIR CONFIG_DIR CC CXX LINK );
+
+use Data::Dumper;
+
+$env = new cons(); # the env on which we will be working for all pk3s (NOTE: maybe we need several ctually)
+
+$hcf_do_exec = 1;
+sub do_command($)
+{
+ printf("@_[0]\n");
+ if ($hcf_do_exec)
+ {
+ system("@_[0]");
+ }
+}
+
+sub build_pk3 {
+
+ sub launch {
+ #print "In launch\n";
+ $Data::Dumper::Indent = 2;
+ #print Dumper(@_);
+ $tmpdir = "/tmp/pk3-builder$$";
+ do_command("rm -rf $tmpdir");
+
+ ($target, $sets) = @_;
+ $base=`basename $target`; chomp($base);
+ $dirname=`dirname $target`; chomp($dirname);
+
+ foreach (@{$sets})
+ {
+ ($sourcepath, $destpath, $file) = @{$_};
+ #print "source: $sourcepath dest: $destpath file: $file\n";
+ do_command("mkdir -p $tmpdir/$destpath");
+ if ($sourcepath =~ /#.*/)
+ {
+ #print "$sourcepath is absolute\n";
+ $sourcepath =~ s/#//;
+ if (ref($file))
+ {
+ foreach(@{$file})
+ {
+ do_command("cp $sourcepath/$_ $tmpdir/$destpath/$_");
+ }
+ }
+ else
+ {
+ do_command("cp $sourcepath/$file $tmpdir/$destpath/$file");
+ }
+ }
+ else
+ {
+ #print "$sourcepath in linked dir\n";
+ if (ref($file))
+ {
+ foreach(@{$file})
+ {
+ do_command("cp $BUILD_DIR/$sourcepath/$_ $tmpdir/$destpath/$_");
+ }
+ }
+ else
+ {
+ do_command("cp $BUILD_DIR/$sourcepath/$file $tmpdir/$destpath/$file");
+ }
+ }
+ }
+
+ do_command("cd $tmpdir ; zip -r $base *");
+ do_command("mkdir -p $BUILD_DIR/$dirname");
+ do_command("cp $tmpdir/$base $BUILD_DIR/$target");
+ do_command("rm -rf $tmpdir");
+
+ return 1;
+ }
+
+ # extract the parameters
+ ($target, $sets) = @_;
+
+ $base=`basename $target`; chomp($base);
+ $dirname=`dirname $target`; chomp($dirname);
+
+ # the build command is stored and called later on by cons
+ # this makes it impossible to have several build_pk3 working together
+ # there is probably a cleaner solution than this hack, but this works
+ $target_uniquename="target_$base";
+ $target_uniquename=~s/\.//g;
+ eval("\$$target_uniquename=\$target");
+ $sets_uniquename="sets_$base";
+ $sets_uniquename=~s/\.//g;
+ eval("\$$sets_uniquename=\$sets");
+ #print "name: $target_uniquename after the hack: $target_pak8pk3";
+
+ # don't pass @{@_} .. since this will be called during the process
+ $command = "[perl] &launch( \$$target_uniquename, [ \@{\$$sets_uniquename} ] )";
+ #print "$command\n";
+
+ foreach(@{$sets})
+ {
+ ($sourcepath, $destpath, $file) = @{$_};
+ if (ref($file))
+ {
+ foreach(@{$file})
+ {
+ Depends $env $target, $sourcepath . '/' . $_;
+ }
+ }
+ else
+ {
+ Depends $env $target, $sourcepath . '/' . $file;
+ }
+ }
+ Command $env $target, $command;
+ Install $env $INSTALL_DIR . "/$dirname", $target;
+}
+
+# quick rundown on the syntax:
+# <target file>, [ <fileset1>, <fileset2>, ... ]
+# where <fileset>: [ <source directory>, <directory in zip>, <file1>, <file2>, .. ]
+
+build_pk3('baseq3/pak8.pk3',
+ [ [ '#../../../media/Q3Media-1.32/baseq3/menu/art', 'menu/art', 'pblogo.tga' ],
+ [ '../Q3/q3_ui/q3_ui', 'vm', 'ui.qvm' ],
+ [ '../Q3/cgame/cgame', 'vm', 'cgame.qvm' ],
+ [ '../Q3/game/game', 'vm', 'qagame.qvm' ] ]
+);
+
+build_pk3('missionpack/pak3.pk3',
+ [ [ '#../ui', 'ui', [ 'createserver.menu', 'joinserver.menu', 'filter.menu', 'punkbuster.menu', 'menus.txt' ] ],
+ [ '../TA/ui/ui', 'vm', 'ui.qvm' ],
+ [ '../TA/cgame/cgame', 'vm', 'cgame.qvm' ],
+ [ '../TA/game/game', 'vm', 'qagame.qvm' ],
+ [ '#../../../media/Q3Media-1.32/missionpack/scripts', 'scripts', 'models2.shader' ] ]
+);
diff --git a/code/unix/Conscript-sdk b/code/unix/Conscript-sdk
new file mode 100755
index 0000000..b2a5e23
--- /dev/null
+++ b/code/unix/Conscript-sdk
@@ -0,0 +1,122 @@
+# sdk
+
+Import qw( SDK_NAME Q3_VER CC CXX LINK );
+
+# http://www.devdaily.com/perl/edu/articles/pl010005/pl010005.shtml
+#----------------------------( promptUser )-----------------------------#
+# #
+# FUNCTION: promptUser #
+# #
+# PURPOSE: Prompt the user for some type of input, and return the #
+# input back to the calling program. #
+# #
+# ARGS: $promptString - what you want to prompt the user with #
+# $defaultValue - (optional) a default value for the prompt #
+# #
+#-------------------------------------------------------------------------#
+
+sub promptUser {
+
+ #-------------------------------------------------------------------#
+ # two possible input arguments - $promptString, and $defaultValue #
+ # make the input arguments local variables. #
+ #-------------------------------------------------------------------#
+
+ local($promptString,$defaultValue) = @_;
+
+ #-------------------------------------------------------------------#
+ # if there is a default value, use the first print statement; if #
+ # no default is provided, print the second string. #
+ #-------------------------------------------------------------------#
+
+ if ($defaultValue) {
+ print $promptString, "[", $defaultValue, "]: ";
+ } else {
+ print $promptString, ": ";
+ }
+
+ $| = 1; # force a flush after our print
+ $_ = <STDIN>; # get the input from STDIN (presumably the keyboard)
+
+
+ #------------------------------------------------------------------#
+ # remove the newline character from the end of the input the user #
+ # gave us. #
+ #------------------------------------------------------------------#
+
+ chomp;
+
+ #-----------------------------------------------------------------#
+ # if we had a $default value, and the user gave us input, then #
+ # return the input; if we had a default, and they gave us no #
+ # no input, return the $defaultValue. #
+ # #
+ # if we did not have a default value, then just return whatever #
+ # the user gave us. if they just hit the <enter> key, #
+ # the calling routine will have to deal with that. #
+ #-----------------------------------------------------------------#
+
+ if ("$defaultValue") {
+ return $_ ? $_ : $defaultValue; # return $_ if it has a value
+ } else {
+ return $_;
+ }
+}
+
+sub launch {
+
+ print("Building MOD SDK (version $Q3_VER)\n");
+ print("Before building the mod sdk, your CVS tree must be completely clean!\nIf there are some temporary files and binaries in the tree, they can get included in the distribution and cause problems.\n");
+ $ok = &promptUser("Ready to build mod sdk [y/n]? ");
+ if ($ok ne 'y')
+ {
+ print("aborting\n");
+ return 0;
+ }
+ # create the dirs:
+ $RAW_DATA = "mod-sdk-$Q3_VER";
+ system("rm -rf $RAW_DATA ; mkdir -p $RAW_DATA/code");
+ # actual code
+ system("cp -R cgame game q3_ui ui $RAW_DATA/code");
+ # UI stuff
+ system("cp -R ../ui $RAW_DATA");
+ # tools
+ system("cp -R ../lcc ../q3asm $RAW_DATA");
+ # build scripts
+ system("cp Construct $RAW_DATA/code");
+ system("mkdir $RAW_DATA/code/unix ; cp unix/cons unix/pcons-2.3.1 unix/Conscript-pk3 $RAW_DATA/code/unix");
+ system("cp unix/mod-sdk-data/Makefile $RAW_DATA/code/");
+ # cleanup some stuff
+ system("find $RAW_DATA -name CVS -exec rm -rf {} \\; 2>/dev/null");
+ # test that the build works just fine
+ $host=`hostname`; chomp($host);
+ if ($host ne 'antares')
+ {
+ system("rm -rf /tmp/$RAW_DATA ; cp -R $RAW_DATA /tmp");
+ system("cd /tmp/$RAW_DATA/code ; make");
+ if ($? ne 0)
+ {
+ printf("ERROR: test build failed\n");
+ return 0;
+ }
+ printf("build test successful\n");
+ }
+ # make a setup out of this
+ system("cp -R unix/setup/setup.sh unix/setup/setup.data $RAW_DATA");
+ # TODO: splash!
+ system("cp unix/mod-sdk-data/setup.xml unix/mod-sdk-data/config.sh unix/mod-sdk-data/postinstall.sh $RAW_DATA/setup.data");
+ # mod-sdk-data/Q3A_EULA.txt is a copy of stuff that is usually in $Q3SETUPMEDIA .. can't be arsed
+ system("cp unix/mod-sdk-data/Q3A_EULA.txt $RAW_DATA");
+ # I love those dirty hacks
+ system("cd $RAW_DATA/setup.data/bin ; ln -s Linux FreeBSD ; ln -s Linux OpenBSD ; ln -s Linux NetBSD");
+ # cleanup some stuff
+ system("find $RAW_DATA -name CVS -exec rm -rf {} \\; 2>/dev/null");
+ # weeee
+ system("unix/mod-sdk-data/makeself/makeself.sh $RAW_DATA $SDK_NAME \"Quake III Arena mod SDK\" ./setup.sh $Q3_VER");
+ return 1;
+}
+
+print("in Conscript-sdk\n");
+
+$env = new cons();
+Command $env "$SDK_NAME", "[perl] &launch()";
diff --git a/code/unix/Conscript-setup b/code/unix/Conscript-setup
new file mode 100755
index 0000000..ef70703
--- /dev/null
+++ b/code/unix/Conscript-setup
@@ -0,0 +1,33 @@
+# setup
+
+Import qw( INSTALL_BASEDIR );
+
+$env = new cons(
+ # add this path so setup script finds brandelf
+ ENV => { PATH => $ENV{PATH} . ":" . $ENV{HOME} . "./usr/bin", },
+);
+
+sub launch {
+ # need to get the correct version (i.e. from SP source)
+ $version = `cat game/q_shared.h | grep Q3_VERSION`;
+ chomp $version;
+ $version =~ s/.*Q3\ (.*)\"/$1/;
+ print("version: $version\n");
+ system("cd unix ; ./build_setup.sh $version");
+
+ return 1;
+}
+
+# no dependency to PB, we get it directly from the tree and not in install/
+Depends $env "$INSTALL_BASEDIR/foo",
+ "$INSTALL_BASEDIR/linuxq3ded",
+ "$INSTALL_BASEDIR/linuxquake3-smp",
+ "$INSTALL_BASEDIR/linuxquake3";
+ # during developement phase, we generate and copy the pk3 on the fly
+ # then those should move to the media tree
+# those have been finalized and moved to the media tree
+# "$INSTALL_BASEDIR/baseq3/pak8.pk3",
+# "$INSTALL_BASEDIR/missionpack/pak3.pk3";
+
+
+Command $env "$INSTALL_BASEDIR/foo", "[perl] &launch()";
diff --git a/code/unix/LinuxSupport/CHANGES-1.32.txt b/code/unix/LinuxSupport/CHANGES-1.32.txt
new file mode 100755
index 0000000..86577af
--- /dev/null
+++ b/code/unix/LinuxSupport/CHANGES-1.32.txt
@@ -0,0 +1,150 @@
+CHANGES 1.32
+
+11/13/2002 - 1.32b
+
+Update to the Linux client, fixing a number of issues:
+
+
+- Text mode install broken on glibc 2.3 (Debian Sid)
+http://bugs.debian.org/cgi-bin/bugreport.cgi?bug=165358
+Current Debian Sid's glibc package break backwards compatibility
+with binaries compiled on older glibc and statically linked.
+This will cause the text mode setup to segfault.
+
+For now you can either:
+ - find a solution to run the graphical gtk-based setup
+ - roll back to glibc 2.2
+ - use an updated setup binary:
+ run the linux setup once, with the --keep option
+ (this will exand all the files to setup.tmp/)
+ get this updated setup file:
+ http://zerowing.idsoftware.com/linux/165358/setup
+ replace setup.tmp/setup.data/bin/Linux/x86/setup by the new one
+ cd to setup.tmp/ and run setup.sh
+
+
+- Current NVidia drivers don't officially support glibc 2.3 systems
+This affects Debian Sid in particular, causing crashes during vid_restart
+The issue is related to the SMP build depending upon libpthreads
+1.32b has both SMP and non-SMP binary. Use quake3-smp to launch
+the SMP version if you want to use it.
+
+
+- SMP and non-SMP builds:
+Default quake3 shortcut will spawn a non-SMP build. Use quake3-smp
+for an SMP-enabled run. From the feedback we got after the 1.32 release,
+the SMP support as designed in the Quake III Arena technology isn't so
+much relevant anymore. It was designed for much lower CPU frequencies
+and lower AGP throughputs. Modern systems won't benefit from SMP. Since
+it didn't make much sense to remove the SMP binary, we have both now.
+
+
+- Fixing broken mouse on Suse:
+Starting with XFree 4.2, the event timestamps returned by the X server
+are no longer matching gettimeofday. And there is currently no API in X
+to provide easy synchronisation. 1.32 introduced improved event timing
+by correcting event time with X server event timestamp (sub-frame timing).
+This worked fine with XFree 4.1, and no longer does. 1.32b adds a safe
+check to work again with XFree 4.2, but basically the input timing is
+back to 1.31 quality.
+
+Some new cvars related to the mouse control:
+in_dgamouse - not new, but be sure to have it set to 1 on any recent system
+in_subframe - completely disables sub-frame timing. If XFree 4.2
+ in_subframe 1 won't hurt, but it won't do anything either.
+in_nograb - for mod developers. Set to 1 and run windowed, Q3 won't be
+ grabbing your mouse pointer (useful for debug breaks)
+
+
+8/26/2002
+
+General:
+
+- new network protocol, 68
+
+- network code:
+ improved fragmented messages handling
+ map change while client loads map no longer causes an 'Invalid .PK3' error
+ map_restart while client loads map no longer causes a reload
+ fixing donedl being ignored after autodl if map_restart'ed
+- the demo command has a list of compatible protocols, it will loop through 66 67 68
+ you can do '/demo four' and it will try four.dm_66 four.dm_67 four.dm_68
+ or you can explicitely give a '/demo demoname.dm_??'
+
+- added mousewheel support in the console:
+ wheel to scroll, ctrl+wheel to scroll faster, shift+wheel to scroll history
+
+- UI in baseq3/ and missionpack/ for PunkBuster configuration
+ punkbuster server in server creation dialog (sv_punkbuster)
+ punkbuster client in server browser (cl_punkbuster)
+ added PB Yes/No to the browsers
+- removed obsolete MPlayer UI stuff
+- bumped server browser to handle up to 4096 servers
+
+- IP maintained in userinfo
+- cl_guid in userinfo (as part of PB integration)
+- printing ports as unsigned ints, no longer printing negative port numbers
+- cleaned up the legacy IP banning code
+ use * for IP pattern matching now instead of 0 (fixes some confusion)
+ ex: 192.246.12.*
+ made it safe from overflowing and crashing
+ NOTE: using PunkBuster's banning system is advised instead though
+- rcon: some fixes to the buffering to avoid overflowing and dropping parts of the message
+- rcon: now supports quoting /rcon g_motd "foo bar"
+- added SVF_CLIENTMASK (0x00000002), works only with <= 32 players
+ set bitmask of players to which send entity
+- pushed cl_maxpackets upper limit to 125
+- added [skipnotify] from RTCW, use to display in the console only, but not on client screen
+ (also fixes pb_msgprefix and pb_sv_msgprefix)
+
+- new cvar sv_lanForceRate (defaults 1):
+ forces LAN clients to the maximum rate instead of accepting client setting
+ (1 is the default behaviour, backward compatible)
+
+- new cvar sv_strictAuth (defaults 1):
+ server side variable to control wether strict CDKEY auth should be performed with the auth server
+ this is required if you want reliable cl_guid for the server (for users tracking)
+
+- filesystem:
+ client re-orders it's pk3s according to the order given by server
+ (fixes some 'Invalid .PK3 files referenced' situations
+
+- fixed invisible entities/players bug (thanks goes to Rick Johnson / Raven for this one!)
+- update x86 VM code (better and safer optimisations) (Richk Johnson / Raven too)
+- clearing client gentity before GAME_INIT call
+- failing vote if there's only one voting client (fixes exploit on 2-player server where one player votes then disconnects, forcing vote to pass)
+
+- added trap_FS_Seek
+
+- renderer fix:
+ if client game code registers a shader after drawsurfaces are generated but before frame is rendered
+ had a one-frame visual glitch (shader indexes messed up)
+- renderer fix:
+ r_roundImagesDown 0 + map q3dm1 -> crash (buffer overflow fix)
+- renderer fix:
+ fixed a crash in widescreen displays (q3dm11)
+- renderer fix:
+ MAX_SHADERS up to 2^12
+- renderer fix:
+ moved screenshot from frontend to backend, fixes broken r_smp 1 screenshots
+
+- TA fixes:
+ MOD_KAMIKAZE MOD_JUICED logging properly to games.log
+ fixed bot taunt spamming
+- fixed typo in scripts/models2.shader (shader error Ursula head)
+
+Win32 specific:
+
+- fixed the DirectInput mouse init procedure
+- rcon:
+ fixed rcon not working on NT/2000/XP workstations that have a long uptime
+
+Linux specific:
+
+- no longer trying to load libMesaVoodooGL.so
+ obsolete code, was confusing when trying to setup correct OpenGL acceleration
+- SMP support in the renderer. Detects CPU count, r_smp 1 default if available. (thanks to Gareth Hughes for contributing this)
+- changed default GL driver from libGL.so to libGL.so.1
+ see LSB 1.2 spec: http://www.linuxbase.org/spec/refspecs/LSB_1.2.0/gLSB/libgl.html
+- Handle Ctrl and Space key together correctly (Ctrl was disabling Space)
+- sub-frame timing of input events (key/mouse) (input timing quality now equivalent to win32)
diff --git a/code/unix/LinuxSupport/INSTALL b/code/unix/LinuxSupport/INSTALL
new file mode 100755
index 0000000..4ea24e7
--- /dev/null
+++ b/code/unix/LinuxSupport/INSTALL
@@ -0,0 +1,52 @@
+ Quake III Arena
+ Quake III: Team Arena
+ Installation instructions
+ ------
+
+What you need:
+
+- retail CD-ROM of Quake III Arena for Windows or linux
+- optionally, retail CD-ROM of Quake III: Team Arena
+- a computer running linux (kernel >= 2.2 glibc >= 2.1)
+- running the client requires X11 and 3D hardware acceleration with OpenGL
+
+Installation:
+
+If you have the retail CD-ROM of Quake III Arena for linux:
+- install Quake III Arena from the CD-ROM
+- run the Point Release installer
+(to buy Quake III Arena for linux online, try http://www.tuxgames.com/)
+
+If you have the retail CD-ROM of Quake III Arena for Windows:
+- run the Point Release installer first
+- now you need to copy the main pak files from the CD-ROM
+ or from your win32 partition. Namely, you need to copy baseq3/pak0.pk3
+ and for Team Arena, missionpack/pak0.pk3
+ see below for more instructions on how to copy the files:
+
+WARNING: Those instructions are provided as a reference,
+depending on your distribution the CD-ROM might not have the same
+mount point. Please refer to your linux documentation for additional
+information.
+
+- open a root shell and cd to the default installation path:
+ cd /usr/local/games/quake3
+- mount the Quake III Arena CD-ROM and install the game content
+ cp /mnt/cdrom/Quake3/baseq3/pak0.pk3 /usr/local/games/quake3/baseq3
+- optionally, mount the Quake III: Team Arena CD-ROM
+ cp /mnt/cdrom/Setup/missionpack/pak0.pk3 /usr/local/games/quake3/missionpack
+
+NOTES:
+- If you are running Quake III Arena and Quake III: Team Arena on a
+ Windows partition, you can save some space by symlinking the game
+ assets to their windows installation:
+ ln -s /win/quake3/baseq3/pak0.pk3 /usr/local/games/quake3/baseq3/pak0.pk3
+ ln -s /win/quake3/missionpack/pak0.pk3 /usr/local/games/quake3/missionpack/pak0.pk3
+ (Windows partition is mounted as /win and Quake III Arena installation is C:\quake3)
+
+Getting Help:
+
+- consult the FAQ in Help/Q3A_FAQ.html
+- head over to the Quake3World forums http://www.quake3world.com/forums
+- consult the list of known issues and tips at
+ http://zerowing.idsoftware.com/linux
diff --git a/code/unix/LinuxSupport/index.html b/code/unix/LinuxSupport/index.html
new file mode 100755
index 0000000..73cfc34
--- /dev/null
+++ b/code/unix/LinuxSupport/index.html
@@ -0,0 +1,283 @@
+<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"><html><head>
+ <title>Linux Quake III Arena Known issues</title></head>
+
+<body text="#000000" bgcolor="#ffffff">
+<font size="+2"><b>Linux Quake III Arena Known issues</b></font><br>
+<i>Known issues and frequently asked questions - 1.32b</i><br>
+<br>
+<a href="mailto:ttimo@idsoftware.com">TTimo</a><br>
+<i>Changes history</i><br>
+2004.2.20 Last update<br>
+2001.6.18 Initial version
+<hr size="1"><br>
+<font size="+1">Current topics</font>
+<ul>
+ <li><a href="#install">Installation instructions</a></li>
+ <li><a href="#setupfiles">What do I do with a .x86.run file?</a></li>
+ <li><a href="#glibc">The setup crashes with <i>Segmentation fault "$setup" "$@" 2>/dev/null</i></a></li>
+ <li><a href="#bsd">Installation on BSD</a></li>
+ <li><a href="#auth">CLIENT_UNKNOWN_TO_AUTH</a></li>
+ <li><a href="#pk3">Sys_Error: Couldn't load default.cfg / Sys_Error: FS_FreeFile(
+NULL )</a></li>
+ <li><a href="#setupbug29h">I get <i>./setup.sh {: ==: binary operator expected</i>
+when running the setup?</a></li>
+ <li><a href="#hints">The game doesn't start, I have performance problems,
+etc.</a></li>
+ <li><a href="#aureal">Aureal sound drivers</a></li>
+ <li><a href="#nosound">The sound doesn't work / sound crashes</a></li>
+ <li><a href="#discuss">Where can I report bugs and discuss about linux
+Quake III Arena?</a></li>
+ <li><a href="#gameso">The *.so files are not in the setups? (<i>cgamei386.so
+qagamei386.so uii386.so</i>)</a></li>
+ <li><a href="#vm_game">I get <i>Sys_Error: Sys_LoadDll(..) failed dlopen()
+completely!</i> when running quake3?</a></li>
+ <li><a href="#3dnow">I have an AMD CPU and a kernel 2.4.*, Quake III Arena
+is slowing down to a complete stop after a while?</a></li>
+ <li><a href="#gamma">How do I set up the gamma correction?</a></li>
+ <li><a href="#browser">Servers don't show up in the ingame browser</a></li>
+ <li><a href="#libsafe">Detected an attempt to write across stack boundary</a></li>
+ <li><a href="#libmesa">libMesaVoodooGL.so</a></li>
+ <li><a href="#UDPwide">Running a LAN dedicated server with multiple network
+interfaces</a></li>
+ <li><a href="#64bits">Setup and execution on 64 bits CPUs</a></li>
+ <li><a href="#links">Links</a></li>
+</ul>
+<hr size="1"><font size="+1"><a name="install"><b>Installation instructions</b></a></font>
+<a name="install"><br>
+</a>
+<p><a name="install">Linux Quake III Arena is using a graphical installer
+(based on Loki software's </a><a href="http://www.icculus.org/loki_setup/">Setup Graphic Installer
+</a>). However, since it's a Point Release, you need a retail CD-ROM of Quake
+III Arena to perform a complete installation (and optionally your Quake III:
+Team Arena CD-ROM). This process is documented in the <a href="http://zerowing.idsoftware.com/linux/q3a/INSTALL">INSTALL</a>file
+(this file is also in the setups, it's default location is /usr/local/games/quake3/INSTALL
+.. you can run the PR setup and read it to finish the installation afterwards).</p>
+<font size="+1"><a name="setupfiles"><b>What do I do with a .x86.run file?</b></a></font>
+<a name="setupfiles"><br>
+</a>
+<p>Those are setup files, meant to be executed. They
+come with graphical installation UI or console installation, depending on
+what's available on your system. You may need to <i>chmod +x file.x86.run</i>
+to make them executable.</p>
+<font size="+1"><a name="glibc"><b>The setup crashes with <i>Segmentation fault "$setup" "$@" 2>/dev/null</i></b></a></font>
+<p>This is happening on glibc 2.3 systems such as RedHat 9 and Suze. The text mode installer will crash. If you can do a graphical installation, make sure you have Gtk1 installed and avoid the text installer altogether.
+If you are doing a remote installation (such as a dedicated server through ssh), you need to use a newer text mode installer. Run the installer with <i>--keep</i> to extract the files
+(look for a <i>setup*</i> directory in the current directory for the extracted setup).
+Then replace <i>setup.data/bin/Linux/x86/setup</i> by <a href="http://zerowing.idsoftware.com/linux/setup-RH9/setup">this newer version</a>. Run <i>setup.sh</i> at top level and things should work fine.</p>
+<p>
+<b>Update</b>: Turns out this solution is working for RTCW and ET, but not for Q3 (because the last q3 setup uses an older version of the installer). Will update a specific solution for Q3 'soon'.
+</p>
+<font size="+1"><a name="bsd"><b>Installation on
+BSD</b></a></font><a name="bsd"><br>
+</a>
+<p><a name="bsd">The linux binaries will run fine on the BSD family (FreeBSD,
+NetBSD and OpenBSD) with the linux binary compatibility software. However
+if you are getting the error message <i>ELF binary type "0" not known</i>
+while installing or trying to run the binaries, that means you need to run
+<i>brandelf</i> on the files.</a></p>
+<p><a name="bsd">If it's a setup problem, proceed with the following steps:
+</a></p>
+<pre><a name="bsd">./linuxq3apoint-1.31.x86.run --keep<br>brandelf -t Linux setup.tmp/setup.data/bin/FreeBSD/x86/setup<br>cd setup.tmp<br>sh ./setup.sh<br></a></pre>
+<p><a name="bsd">The --keep option will extract the files and leave them
+somewhere below your current directory. Depending on the game (Q3 or RTCW)
+and the setup version, your mileage may vary (setup.tmp or another directory).</a></p>
+<p><a name="bsd">The game binaries might need to be brandelf'ed too, with
+a command such as</a></p>
+<pre><a name="bsd">brandelf -t Linux /usr/local/games/quake3/quake3.x86<br></a></pre>
+<a name="bsd"></a><font size="+1"><a name="auth"><b>CLIENT_UNKNOWN_TO_AUTH</b></a></font>
+<a name="auth"><br>
+</a>
+<p><a name="auth">Graeme Devine recently updated his <i>.plan</i> with very
+complete </a><a href="http://www.webdog.org/cgi-bin/finger.plm?id=279&amp;time=20011210020942" target="_new">information about CLIENT_UNKNOWN_TO_AUTH errors</a>.</p>
+<p>
+See some additional information from the <a href="http://www.gameadmins.com/modules.php?name=Mail_List">gameadmins.com mailing list</a>:
+</p>
+<pre>If the server you are playing on and the auth server don't see you as the
+same IP (for instance you are trying to play on a public internet server
+that's on your LAN, and your internet access is using NAT), then it won't
+work.
+
+It used to work in 1.31, and it doesn't in 1.32. PunkBuster requires
+reliable auth of the players. What you can do:
+
+- run a server with sv_strictauth 0 and you'll be able to join your
+server. This will be the same behaviour as 1.31
+
+- connect to a server on the internet before you connect to your local
+server (this will trigger your IP into the cache of the auth server for
+15mn and let you in to your local server).
+
+- setup two NATs, one for your client one for your server and make sure
+your server and Id's auth see the same IP. (this one ain't for network
+setup newbies)
+</pre>
+<font size="+1"><a name="pk3"><b>Sys_Error: Couldn't load default.cfg / Sys_Error:
+FS_FreeFile( NULL )</b></a></font><br>
+<p>If you get one of these errors after installing Quake III Arena or Return
+To Castle Wolfenstein, it means that the engine didn't find all the required
+.pk3 files. Either you didn't copy them, or you copied them to the wrong
+place. Check the INSTALL instructions for the game for more details, make
+sure they are in baseq3/ for quake3 (missionpack/ for TA files) and main/
+for Return To Castle Wolfenstein.</p>
+<font size="+1"><a name="setupbug29h"><b>I get <i>./setup.sh {: ==: binary
+operator expected</i> when running the setup?</b></a></font><a name="setupbug29h"><br>
+</a>
+<p><a name="setupbug29h">This is a known issue to 1.29h setups and prior.
+It happens on systems with bash version &lt; 2.*. There are several solutions:<br>
+- Upgrade bash to something more recent and run the setup again - Run the
+setup with the --keep option. It will fail but it will leave a <i>setup-full</i>
+directory. You can then copy the files manually from that dir. - Once you
+used the --keep option above, you can edit setup.sh and replace occurences
+of == by =. Then run setup.sh and the installer will execute.</a></p>
+<a name="setupbug29h"></a><font size="+1"><a name="hints"><b>The game doesn't
+start, I have performance problems, etc.</b></a></font><br>
+<p>The first thing to do is to check on the forums and various FAQs (this
+one, but there are others. See the <a href="#links">links</a>). The Quake3World
+forums have a great search function.</p>
+<p>Before reporting the problem to <a href="mailto:ttimo@idsoftware.com">me</a>
+make sure it's an issue with the game, and not an issue with your OS/OpenGL/sound
+configuration. Common OS issues are listed in this FAQ. You should make sure
+you have OpenGL configured correctly (by checking if <i>gears</i>is running
+for instance, and how well it runs). And see if non-Id linux games are running
+fine too.</p>
+<p>When you are going to report a bug, first make sure you are using the
+latest version of the game. Include the game version in your report.</p>
+<p>Include general information about your OS:<br>
+</p>
+<ul>
+ <li>Motherboard brand, CPU type, RAM</li>
+ <li>distribution name and version</li>
+ <li>kernel / OS info (from <i>uname -a</i>)</li>
+ <li>libc version (<i>ls -l /lib/libc.so.*</i>)<br>
+ please specify if you can if the libc is your distribution's standard
+version, or if you compiled yourself, and what binary target was used (x86,
+or AMD, i686 etc.)</li>
+</ul>
+<p>If it's a problem with the client, send the output of <i>glxinfo</i>.</p>
+<p>If you have an nvidia board, send the output of <i>cat /proc/nv/card0</i>
+</p>
+<p>Send output of the run:<br>
+run the game with <i>+set developer 1</i> option, and send the output. You
+can do something like <i>quake3 +set developer 1 &amp;&gt;q3run.log</i>.</p>
+<p>If it's a crash, you can send a backtrace of the game running through
+<i>gdb</i>.</p>
+<p>You can also send a log of the game running with <i>strace</i>:</p>
+<pre>cd /usr/local/games/quake3<br>strace -o ~/strace.log ./quake3.x86<br></pre>
+<p>NOTE: please <b>avoid</b> sending me the <i>strace</i> of <i>/usr/local/games/quake3/quake3</i>,
+which is a shell script wrapper and probably no interest to your problem.
+</p>
+<font size="+1"><a name="aureal"><b>Aureal sound drivers</b></a></font><a name="aureal"><br>
+</a>
+<p><a name="aureal">It seems that some versions of the Aureal sound drivers
+don't work right with Q3. Last I heard, a kernel upgrade to 2.4.17 + Aureal
+1.1.3, and/or using the old 1.1.1 drivers from </a><a href="http://aureal.sourceforge.net/" target="_new">Aureal's website</a>fixed
+the problem.</p>
+<p>If you need to know more about this, have a look at this <a href="http://www.quake3world.com/ubb/Forum15/HTML/001348.html" target="_new">Q3W forum thread</a>.</p>
+<font size="+1"><a name="nosound"><b>The sound doesn't work / sound crashes</b></a></font><br>
+<p>The first thing to check is that it is actually a sound related. Run
+the game with <i>+set s_initsound 0</i> and see what happens. All problems
+reported so far about sound turned out to be OS/drivers. Listed below:</p>
+<p>On some Mandrake distributions:<br>
+Check if you are running the enlightenment sound daemon (esd). With <i>ps
+aux | grep esd</i>for instance. It is a multiplexer for /dev/dsp, and might
+block use of /dev/dsp by Quake III Arena. You can disable esd with <i>esdctl
+stop</i> (as root).</p>
+<p><b><a href="http://www.linux-mandrake.com/">Mandrake 8</a></b>'s default
+sound drivers seem broken, installing the <a href="http://www.alsa-project.org/" target="_new">Alsa drivers</a> or the
+ <a href="http://www.opensound.com/">http://www.opensound.com</a> drivers
+fixes the problem.</p>
+<p>Some beta <a href="http://www.alsa-project.org/" target="_new">Alsa drivers</a>
+have been reported to crash with Q3. Non-beta ones are fine.</p>
+<p>VIA chipset and AC97 driver:<br>
+This combination is known to have various issues. They have been fixed in
+recent drivers (thanks to Arne Schmitz for the heads up):<br>
+</p>
+<pre>http://sourceforge.net/projects/gkernel has got the up to date version of <br>the AC97 kernel driver. The current version can be found here:<br><br>http://prdownloads.sourceforge.net/gkernel/via82cxxx-1.1.15.tar.gz<br><br>It has working mmap sound, so Q3 shouldn't be a problem any more.<br></pre>
+(thanks to Arne Schmitz for the heads up)<p></p>
+
+<font size="+1"><a name="discuss"><b>Where can I report bugs and discuss about linux Quake III
+Arena?</b></a></font><a name="discuss"><br>
+</a>
+<p><a name="discuss">Reports bugs to </a><a href="mailto:bugs@idsoftware.com">bugs@idsoftware.com</a>. If you are pretty
+sure this is a linux-only issue, you can shorten the loop by emailing <a href="mailto:ttimo@idsoftware.com">ttimo@idsoftware.com</a> directly.</p>
+<p>You will find the discussion forums for linux Quake III Arena on <a href="http://www.quake3world.com/cgi-bin/forumdisplay.cgi?action=topics&amp;forum=*nix+Discussion&amp;number=15&amp;DaysPrune=30&amp;LastLogin=">
+Quake3World forums</a>. There is for sure a lot of other places to talk about
+linux Quake III Arena, but this is the one we read regularly to track bugs
+and common issues.</p>
+<font size="+1"><a name="gameso"><b>The *.so files are not in the setups?
+(<i>cgamei386.so qagamei386.so uii386.so</i>)</b></a></font><a name="gameso"><br>
+</a>
+<p><a name="gameso">If you still have <i>baseq3/*.so</i> and <i>missionpack/*.so</i>
+files, then those come from the earlier 1.27g beta installation and you should
+REMOVE them. They were provided in 1.27g to go around a bug in the VM code,
+which made win32 VMs incompatible with linux. This problem has been fixed
+and the two files are no longer required.</a></p>
+<p><a name="gameso">If you are upgrading from 1.27g, it is likely that your
+<i>q3config.cfg</i> files are set to use the native libraries (*.so files)
+instead of the bytecode. Run quake3 with the following options to set things
+right:<br>
+<i>quake3 +set vm_game 2 +set vm_cgame 2 +set vm_ui 2</i></a></p>
+<a name="gameso"></a><font size="+1"><a name="vm_game"><b>I get <i>Sys_Error:
+Sys_LoadDll(..) failed dlopen() completely!</i>when running quake3?</b></a></font><a name="vm_game"><br>
+</a>
+<p><a name="vm_game">Try running quake3 with the following options:<br>
+<i>quake3 +set vm_game 2 +set vm_cgame 2 +set vm_ui 2</i><br>
+You should also read the </a><a href="#gameso">above answer</a>.</p>
+<font size="+1"><a name="3dnow"><b>I have an AMD CPU and a kernel 2.4.*,
+Quake III Arena is slowing down to a complete stop after a while?</b></a></font><br>
+<p>It seems the 3DNow! copy routines have issues with the southbridge chip
+in the KT133A, this results in performances degrading while playing for a
+while. Re-compile your kernel without 3DNow! instructions to avoid the problem,
+and wait for newer kernels with better support for 3DNow! / KT133A.</p>
+<font size="+1"><a name="gamma"><b>How do I set up the gamma correction?</b></a></font><br>
+<p>Starting with 1.29h, you can set the gamma correction with the brightness
+slider in the graphical menu (under setup). On some older systems which don't
+have the appropriate XFree86 extensions, you might have to set <b>r_gamma</b>
+manually from the console, and issue a <b>vid_restart</b> command.</p>
+<font size="+1"><a name="browser"><b>Servers don't show up in the ingame
+browser</b></a></font>
+<p>The reason for this has not been clearly identified yet, seems to be related
+to upgrade from older versions. Deleting ~/.q3a/baseq3/q3config.cfg fixes
+the problem (you will have to reconfigure your bindings)</p>
+<font size="+1"><a name="libsafe"><b>Detected an attempt to write across
+stack boundary</b></a></font>
+<p>If Quake III Arena exits with the error "Detected an attempt to write
+across stack boundary", this probably means that you are running libsafe
+on this system. Quake III Arena is compiled with some options that confuse
+libsafe, you should disable it before running. See <a href="http://www.mudos.org/?faq" target="_new">this page</a> for more details.
+</p>
+<font size="+1"><a name="libmesa"><b>libMesaVoodooGL.so</b></a></font>
+<p>The GL driver for Voodoo cards (libMesaVoodoGL.so) used to be distributed
+in older Q3 setups. <b>This is no longer the case.</b> If you have this .so
+in your Quake III Arena directory (<i>/usr/local/games/quake3</i>), you should
+remove it. Any recent/decent linux distribution should support your Voodoo
+card out of the box, otherwise it is recommended that you setup XFree 4.*
+and the correct DRI infrastructure for it.</p>
+<font size="+1"><a name="UDPwide"><b>Running a LAN dedicated server with
+multiple network interfaces</b></a></font>
+<p>A LAN dedicated server will use the <i>net_ip</i> cvar to identify the
+NIC it is going to use (default is "localhost"). As it only opens one socket,
+it is not possible to have a server broadcast it's packets on all the NICs.
+This can be a problem if the server is serving games for a LAN and runs several
+NICs to access the various sub networks.<br>
+</p>
+<p>The following Linux kernel patch (2.4.19) was provided by Rogier Mulhujzen
+and John Tobin, it will force broadcasts to be emitted on all interfaces:<br>
+<a href="http://zerowing.idsoftware.com/linux/q3a/udp_wide_broadcast.patch">udp_wide_broadcast.patch</a><br>
+<a href="http://zerowing.idsoftware.com/linux/q3a/udp_wide_README.txt">udp_wide_README.txt</a><br>
+The equivalent <a href="http://www.bsdchicks.com/patches/">patch for FreeBSD</a>
+is available too.<br>
+ </p>
+<font size="+1"><a name="64bits"><b>Setup and execution on 64 bits CPUs</b></a></font><br>
+<p>If you are running Linux on a 64 bit CPU (such as AMD's Opteron), and if your system is backwards compatible so that it can execute 32 bits x86 binaries, then the regular Quake III Arena binaries should work (your mileage may vary).</p>
+<p>It's likely that the installer scripts will get confused though, and will refuse to install giving you an error: "This installation doesn't support glibc-2.1 on Linux / unknown". You will have to extract the game files manually by passing --keep on the command line when running the setup script. Once the files are unpacked, you will need to copy them manually to /usr/local/games. You probably want to have a working installation to refer to while doing this. This also applies to RTCW and ET</p>
+<font size="+1"><a name="links"><b>Links</b></a></font><br>
+<p>The <a href="http://www.icculus.org/lgfaq" target="_new">Linux Gamer's
+faq</a> is a very good resource for general Linux Gaming topics.</p>
+<p>Also at <a href="http://www.icculus.org/" target="_new">icculus.org</a>,
+the <a href="http://www.icculus.org/lgfaq/loki/q3faq.html" target="_new">old
+Q3 FAQ from Loki Software</a>.</p>
+<p><a href="http://www.quake3world.com/ubb/Forum15/HTML/000529.html">Quake3World's
+linux FAQ</a></p>
+<br>
+</body></html>
diff --git a/code/unix/LinuxSupport/udp_wide_README.txt b/code/unix/LinuxSupport/udp_wide_README.txt
new file mode 100755
index 0000000..7c0d7b6
--- /dev/null
+++ b/code/unix/LinuxSupport/udp_wide_README.txt
@@ -0,0 +1,20 @@
+UDP Wide Broadcast Patch for Kernel 2.4.19
+
+Main purpose is to allow Quake 3 servers, and all games powered by the
+engine, to be visible in the server browser when the servers are being run on
+aliased IP addresses and multiple NICs using the +set net_ip option.
+
+To apply the patch run "patch -p1 < udp_wide_broadcast.patch" from your
+source directory and recompile.
+
+Add "echo "1" > /proc/sys/net/ipv4/udp_wide_broadcast" to one of your startup
+scripts, i.e./etc/rc.d/rc.local, and run thousands of servers from your
+computer.
+
+
+Patch by:
+Rogier Mulhujzen <linux@bsdchicks.com>
+John Tobin <ogre@sirinet.net>
+
+A patch with the same functionality for FreeBSD is available from
+http://www.bsdchicks.com/patches
diff --git a/code/unix/LinuxSupport/udp_wide_broadcast.patch b/code/unix/LinuxSupport/udp_wide_broadcast.patch
new file mode 100755
index 0000000..b6f86eb
--- /dev/null
+++ b/code/unix/LinuxSupport/udp_wide_broadcast.patch
@@ -0,0 +1,57 @@
+--- linux/include/linux/sysctl.h.orig 2002-08-17 19:52:27.000000000 -0500
++++ linux/include/linux/sysctl.h 2002-08-17 19:53:00.000000000 -0500
+@@ -291,7 +291,8 @@
+ NET_IPV4_NONLOCAL_BIND=88,
+ NET_IPV4_ICMP_RATELIMIT=89,
+ NET_IPV4_ICMP_RATEMASK=90,
+- NET_TCP_TW_REUSE=91
++ NET_TCP_TW_REUSE=91,
++ NET_UDP_WIDE_BROADCAST=92
+ };
+
+ enum {
+--- linux-modified/net/ipv4/sysctl_net_ipv4.c.orig 2002-08-17 19:48:19.000000000 -0500
++++ linux-modified/net/ipv4/sysctl_net_ipv4.c 2002-08-17 19:52:03.000000000 -0500
+@@ -45,6 +45,9 @@
+ extern int inet_peer_gc_mintime;
+ extern int inet_peer_gc_maxtime;
+
++/* From udp.c */
++extern int sysctl_udp_wide_broadcast;
++
+ #ifdef CONFIG_SYSCTL
+ static int tcp_retr1_max = 255;
+ static int ip_local_port_range_min[] = { 1, 1 };
+@@ -221,6 +224,8 @@
+ &sysctl_icmp_ratemask, sizeof(int), 0644, NULL, &proc_dointvec},
+ {NET_TCP_TW_REUSE, "tcp_tw_reuse",
+ &sysctl_tcp_tw_reuse, sizeof(int), 0644, NULL, &proc_dointvec},
++ {NET_UDP_WIDE_BROADCAST, "udp_wide_broadcast",
++ &sysctl_udp_wide_broadcast, sizeof(int), 0644, NULL, &proc_dointvec},
+ {0}
+ };
+
+--- linux-modified/net/ipv4/udp.c.orig 2002-08-17 19:40:59.000000000 -0500
++++ linux-modified/net/ipv4/udp.c 2002-08-17 23:37:47.000000000 -0500
+@@ -94,6 +94,8 @@
+ #include <net/inet_common.h>
+ #include <net/checksum.h>
+
++int sysctl_udp_wide_broadcast = 0;
++
+ /*
+ * Snmp MIB for the UDP layer
+ */
+@@ -272,9 +274,10 @@
+ if ((s->num != hnum) ||
+ (s->daddr && s->daddr!=rmt_addr) ||
+ (s->dport != rmt_port && s->dport != 0) ||
+- (s->rcv_saddr && s->rcv_saddr != loc_addr) ||
+- (s->bound_dev_if && s->bound_dev_if != dif))
++ !(sysctl_udp_wide_broadcast || !(s->rcv_saddr && s->rcv_saddr != loc_addr)) ||
++ (s->bound_dev_if && s->bound_dev_if != dif)) {
+ continue;
++ }
+ break;
+ }
+ return s;
diff --git a/code/unix/Makefile b/code/unix/Makefile
new file mode 100755
index 0000000..8d9f3c1
--- /dev/null
+++ b/code/unix/Makefile
@@ -0,0 +1,2119 @@
+#
+# Quake3 Unix Makefile
+#
+# Currently build for the following:
+# Linux i386 (full client)
+# Linux Alpha (dedicated server only) (TTimo: dropped)
+# FreeBSD i386 (dedicated server only) (TTimo: maybe worth updating)
+# SGI IRIX (full client) (TTimo: who's in charge - dropped otherwise)
+# Linux PPC (full client) (TTimo: dropped)
+#
+# Nov '98 by Zoid <zoid@idsoftware.com>
+#
+# Loki Hacking by Bernd Kreimeier
+# and a little more by Ryan C. Gordon.
+# and a little more by Rafael Barrero
+#
+# GNU Make required
+#
+
+PLATFORM=$(shell uname|tr A-Z a-z)
+PLATFORM_RELEASE=$(shell uname -r)
+
+###
+### These paths are where you probably want to change things
+###
+
+# Where we are building from (where the source code should be!)
+MOUNT_DIR=../
+
+# Where we are building to, libMesaVoodooGL.so.3.3 should be here, etc.
+# the demo pk3 file should be here in demoq3/pak0.pk3 or baseq3/pak0.pk3
+BDIR=$(MOUNT_DIR)/../run
+
+
+# Build name
+# BUILD_NAME=$(BUILD_NAME)
+BUILD_NAME=quake3.x86
+
+
+
+#############################################################################
+##
+## You shouldn't have to touch anything below here
+##
+#############################################################################
+
+BASEQ3_DIR=$(BDIR)/baseq3
+
+BD=debug$(ARCH)$(GLIBC)
+BR=release$(ARCH)$(GLIBC)
+CDIR=$(MOUNT_DIR)/client
+SDIR=$(MOUNT_DIR)/server
+RDIR=$(MOUNT_DIR)/renderer
+CMDIR=$(MOUNT_DIR)/qcommon
+UDIR=$(MOUNT_DIR)/unix
+GDIR=$(MOUNT_DIR)/game
+CGDIR=$(MOUNT_DIR)/cgame
+BAIDIR=$(GDIR)
+BLIBDIR=$(MOUNT_DIR)/botlib
+NDIR=$(MOUNT_DIR)/null
+UIDIR=$(MOUNT_DIR)/ui
+Q3UIDIR=$(MOUNT_DIR)/q3_ui
+FTDIR=$(MOUNT_DIR)/ft2
+JPDIR=$(MOUNT_DIR)/jpeg-6
+SPLNDIR=$(MOUNT_DIR)/splines
+
+# extract version info
+VERSION=$(shell ./extract_ver.pl)
+RPM_RELEASE=1
+
+# NOTE: used by dcp rcp targets, not referenced
+#TESTDIR=/home/timo/Id/Quake3-loki/run
+
+#############################################################################
+# SETUP AND BUILD -- LINUX
+#############################################################################
+
+## Defaults
+DLL_ONLY=false
+# bk010215 - TODO - add all defaults / kill Ryan
+
+
+ifeq ($(PLATFORM),linux)
+
+ ifneq (,$(findstring libc6,$(shell if [ -e /lib/libc.so.6* ];then echo libc6;fi)))
+ GLIBC=-glibc
+ else
+ GLIBC=
+ endif #libc6 test
+
+ ifneq (,$(findstring alpha,$(shell uname -m)))
+ ARCH=axp
+ RPMARCH=alpha
+ VENDOR=dec
+ else
+ ifneq (,$(findstring ppc,$(shell uname -m)))
+ #used for linux builds
+ MESADIR=/usr/src/DRI-CVS/xc/xc/
+ ARCH=ppc
+ RPMARCH=ppc
+ VENDOR=unknown
+ DLL_ONLY=true
+ else #default to i386
+ MESADIR=../Mesa/
+ ARCH=i386
+ RPMARCH=i386
+ VENDOR=unknown
+ DLL_ONLY=false
+ endif
+ endif
+
+ # bk001205: no mo' -I/usr/include/glide, no FX
+ # bk001205: no mo' -Dstricmp=strcasecmp, see q_shared.h
+ BASE_CFLAGS = -pipe -fsigned-char
+ # rcg010216: DLL_ONLY for PPC
+ ifeq ($(strip $(DLL_ONLY)),true)
+ BASE_CFLAGS += -DDLL_ONLY
+ endif
+
+
+ #GL_CFLAGS = -I$(MESADIR)/include -I/usr/X11R6/include
+ GL_CFAGS = -I/usr/X11R6/include
+
+ # bk001204 - need -O for -Wall for uninitialized
+ # bk001205 - took out -O to get assertions (NDEBUG)
+ # bk001206 - MALLOC_CHECK in addition to ZONE_DEBUG
+ # TTimo 03/30/2001 temporary took out -Werror for initial merge
+ DEBUG_CFLAGS = $(BASE_CFLAGS) -g -Wall -Werror
+ DEBUG_CFLAGS += -DNO_MOUSEGRAB
+ DEBUG_CFLAGS += -O
+# DEBUG_CFLAGS=$(BASE_CFLAGS) -g -Wall -O
+ ifeq ($(ARCH),axp)
+ CC=pgcc
+ RELEASE_CFLAGS=$(BASE_CFLAGS) -DNDEBUG -O6 -ffast-math -funroll-loops -fomit-frame-pointer -fexpensive-optimizations
+ else
+ ifeq ($(ARCH),ppc)
+ NEWPGCC=/loki/global/ppc/bin/gcc
+ CC=$(NEWPGCC)
+ RELEASE_CFLAGS=$(BASE_CFLAGS) -DNDEBUG -O6 -fomit-frame-pointer -pipe -ffast-math -malign-loops=2 -malign-jumps=2 -malign-functions=2 -fno-strict-aliasing -fstrength-reduce
+ else
+ #NEWPGCC=/usr/local/gcc-2.95.2/bin/gcc # bk001205
+ #NEWPGCC=/loki/global/x86/bin/gcc
+ NEWPGCC=/usr/bin/gcc
+ CC=$(shell if [ -f $(NEWPGCC) ]; then echo $(NEWPGCC); else echo pgcc; fi )
+ CXX=/usr/bin/g++
+# TTimo: legacy RELEASE_CFLAGS
+# NOTE: the -fomit-frame-pointer option leads to an unstable binary on my test box if it was built on the main box
+# but building on the Mdk 7.2 baseline seems to work
+ RELEASE_CFLAGS=$(BASE_CFLAGS) -DNDEBUG -O6 -mcpu=pentiumpro -march=pentium -fomit-frame-pointer -pipe -ffast-math -malign-loops=2 -malign-jumps=2 -malign-functions=2 -fno-strict-aliasing -fstrength-reduce
+# TTimo: use this for building on P3 gcc 2.95.3 libc2.2 for all targets (experimental! -fomit-fram-pointer removed)
+# RELEASE_CFLAGS=$(BASE_CFLAGS) -DNDEBUG -O6 -mcpu=pentiumpro -march=pentium -pipe -ffast-math -malign-loops=2 -malign-jumps=2 -malign-functions=2 -fno-strict-aliasing -fstrength-reduce
+ endif
+ endif
+
+ LIBEXT=a
+
+ SHLIBEXT=so
+ SHLIBCFLAGS=-fPIC
+ SHLIBLDFLAGS=-shared $(LDFLAGS)
+
+ ARFLAGS=ar rv
+ RANLIB=ranlib
+
+ THREAD_LDFLAGS=-lpthread
+ LDFLAGS=-ldl -lm
+ GLLDFLAGS=-L/usr/X11R6/lib -lX11 -lXext -lXxf86dga -lXxf86vm
+
+ ifeq ($(ARCH),axp)
+ TARGETS=\
+ $(B)/$(PLATFORM)q3ded
+ else
+ TARGETS=\
+ $(B)/$(PLATFORM)quake3 \
+ $(B)/$(PLATFORM)quake3-smp \
+ $(B)/$(PLATFORM)q3ded \
+ $(B)/baseq3/cgame$(ARCH).$(SHLIBEXT) \
+ $(B)/baseq3/qagame$(ARCH).$(SHLIBEXT) \
+ $(B)/baseq3/ui$(ARCH).$(SHLIBEXT) \
+ $(B)/missionpack/cgame$(ARCH).$(SHLIBEXT) \
+ $(B)/missionpack/qagame$(ARCH).$(SHLIBEXT) \
+ $(B)/missionpack/ui$(ARCH).$(SHLIBEXT) \
+# $(B)/baseq3/vm/qagame.qvm \
+# $(B)/baseq3/vm/cgame.qvm \
+# $(B)/baseq3/vm/ui.qvm \
+# $(B)/missionpack/vm/qagame.qvm \
+# $(B)/missionpack/vm/cgame.qvm \
+# $(B)/missionpack/vm/ui.qvm
+ endif
+
+## $(B)/$(PLATFORM)q3static \
+
+else # ifeq Linux
+
+#############################################################################
+# SETUP AND BUILD -- FREEBSD
+#############################################################################
+
+ifeq ($(PLATFORM),freebsd)
+
+GLIBC= #libc is irrelevant
+
+ifneq (,$(findstring alpha,$(shell uname -m)))
+ARCH=axp
+RPMARCH=alpha
+VENDOR=dec
+else #default to i386
+ARCH=i386
+RPMARCH=i386
+VENDOR=unknown
+endif #alpha test
+
+
+BASE_CFLAGS = -pipe
+
+GL_CFLAGS = -I$(MESADIR)/include -I/usr/X11R6/include
+
+DEBUG_CFLAGS=$(BASE_CFLAGS) -g -Wall -Werror
+ifeq ($(ARCH),axp)
+CC=pgcc
+RELEASE_CFLAGS=$(BASE_CFLAGS) -DNDEBUG -O6 -ffast-math -funroll-loops -fomit-frame-pointer -fexpensive-optimizations
+else
+#NEWPGCC=/loki/global/x86/bin/gcc # raistlin012301
+#NEWPGCC=/usr/local/gcc-2.95.2/bin/gcc
+NEWPGCC=/home/raistllin/src/gcc/build/install/bin/gcc
+CC=$(shell if [ -f $(NEWPGCC) ]; then echo $(NEWPGCC); else echo pgcc; fi )
+RELEASE_CFLAGS=$(BASE_CFLAGS) -DNDEBUG -O6 -mcpu=pentiumpro -march=pentium -fomit-frame-pointer -pipe -ffast-math -malign-loops=2 -malign-jumps=2 -malign-functions=2 -fno-strict-aliasing - fstrength-reduce
+endif
+
+LIBEXT=a
+
+SHLIBEXT=so
+SHLIBCFLAGS=-fPIC
+SHLIBLDFLAGS=-shared $(LDFLAGS)
+
+ARFLAGS=ar rv
+RANLIB=ranlib
+
+THREAD_LDFLAGS=-lpthread
+# don't need -ldl (FreeBSD)
+LDFLAGS=-lm
+#GLLDFLAGS=-L/usr/X11R6/lib -L$(MESADIR)/lib -lGL -lX11 -lXext -lXxf86dga -lXxf86vm
+#GLLDFLAGS=-L/usr/X11/lib -lGL -lX11 -lXext -lm
+GLLDFLAGS=-L/usr/X11R6/lib -lGL -lX11 -lXext -lXxf86dga -lXxf86vm
+
+ifeq ($(ARCH),axp)
+TARGETS=\
+ $(B)/$(PLATFORM)q3ded
+else
+TARGETS=\
+ $(B)/$(PLATFORM)quake3 \
+ $(B)/baseq3/cgame$(ARCH).$(SHLIBEXT) \
+ $(B)/baseq3/qagame$(ARCH).$(SHLIBEXT) \
+ $(B)/baseq3/ui$(ARCH).$(SHLIBEXT) \
+ $(B)/missionpack/cgame$(ARCH).$(SHLIBEXT) \
+ $(B)/missionpack/qagame$(ARCH).$(SHLIBEXT) \
+ $(B)/missionpack/ui$(ARCH).$(SHLIBEXT)
+endif
+
+else # ifeq freebsd
+
+#############################################################################
+# SETUP AND BUILD -- IRIX
+#############################################################################
+
+ifeq ($(PLATFORM),irix)
+
+ARCH=mips #default to MIPS
+VENDOR=sgi
+GLIBC= #libc is irrelevant
+
+CC=cc
+BASE_CFLAGS=-Dstricmp=strcasecmp -Xcpluscomm -woff 1185 -mips3 \
+ -nostdinc -I. -I$(ROOT)/usr/include
+RELEASE_CFLAGS=$(BASE_CFLAGS) -O3
+DEBUG_CFLAGS=$(BASE_CFLAGS) -g
+
+LIBEXT=a
+
+SHLIBEXT=so
+SHLIBCFLAGS=
+SHLIBLDFLAGS=-shared
+
+ARFLAGS=ar rv
+RANLIB=ranlib
+
+LDFLAGS=-ldl -lm
+GLLDFLAGS=-L/usr/X11/lib -lGL -lX11 -lXext -lm
+
+TARGETS=$(B)/sgiquake3 \
+ $(B)/q3ded
+
+else # ifeq IRIX
+
+#############################################################################
+# SETUP AND BUILD -- GENERIC
+#############################################################################
+
+#CC=egcs
+#BASE_CFLAGS=-Dstricmp=strcasecmp
+#DEBUG_CFLAGS=$(BASE_CFLAGS) -g
+#RELEASE_CFLAGS=$(BASE_CFLAGS) -DNDEBUG -O3
+
+LIBEXT=a
+
+SHLIBEXT=so
+SHLIBCFLAGS=-fPIC
+SHLIBLDFLAGS=-shared
+
+ARFLAGS=ar rv
+RANLIB=ranlib
+
+ifeq ($(PLATFORM),freebsd)
+LDFLAGS=-lm
+else
+LDFLAGS=-ldl -lm
+endif # ifeq freebsd
+
+TARGETS=\
+ $(B)/$(PLATFORM)q3ded
+
+endif #Linux
+endif #FreeBSD
+endif #IRIX
+
+DO_CC=$(CC) $(CFLAGS) -o $@ -c $<
+DO_CXX=$(CXX) $(CFLAGS) -o $@ -c $<
+DO_SMP_CC=$(CC) $(CFLAGS) -DSMP -o $@ -c $<
+DO_BOT_CC=$(CC) $(CFLAGS) -DBOTLIB -o $@ -c $< # $(SHLIBCFLAGS) # bk001212
+DO_DEBUG_CC=$(CC) $(DEBUG_CFLAGS) -o $@ -c $<
+DO_SHLIB_CC=$(CC) $(CFLAGS) $(SHLIBCFLAGS) -o $@ -c $<
+DO_SHLIB_DEBUG_CC=$(CC) $(DEBUG_CFLAGS) $(SHLIBCFLAGS) -o $@ -c $<
+DO_AS=$(CC) $(CFLAGS) -DELF -x assembler-with-cpp -o $@ -c $<
+DO_NASM=nasm -f elf -o $@ $<
+DO_DED_CC=$(CC) -DDEDICATED -DC_ONLY $(CFLAGS) -o $@ -c $<
+
+#DO_LCC=$(LCC) -o $@ -S -Wf-target=bytecode -Wf-g -DQ3_VM -I$(CGDIR) -I$(GDIR) -I$(UIDIR) $<
+
+#### DEFAULT TARGET
+default:build_debug
+
+debug: build_debug
+release: build_release
+
+build_debug:
+ $(MAKE) targets B=$(BD) CFLAGS="$(DEBUG_CFLAGS)"
+
+build_release:
+ $(MAKE) targets B=$(BR) CFLAGS="$(RELEASE_CFLAGS)"
+
+#Build both debug and release builds
+all:build_debug build_release
+
+targets:makedirs $(TARGETS)
+
+makedirs:
+ @if [ ! -d $(B) ];then mkdir $(B);fi
+ @if [ ! -d $(B)/client ];then mkdir $(B)/client;fi
+ @if [ ! -d $(B)/ded ];then mkdir $(B)/ded;fi
+ @if [ ! -d $(B)/ref ];then mkdir $(B)/ref;fi
+ @if [ ! -d $(B)/ft2 ];then mkdir $(B)/ft2;fi
+ @if [ ! -d $(B)/baseq3 ];then mkdir $(B)/baseq3;fi
+ @if [ ! -d $(B)/baseq3/cgame ];then mkdir $(B)/baseq3/cgame;fi
+ @if [ ! -d $(B)/baseq3/game ];then mkdir $(B)/baseq3/game;fi
+ @if [ ! -d $(B)/baseq3/ui ];then mkdir $(B)/baseq3/ui;fi
+ @if [ ! -d $(B)/baseq3/vm ];then mkdir $(B)/baseq3/vm;fi
+ @if [ ! -d $(B)/missionpack ];then mkdir $(B)/missionpack;fi
+ @if [ ! -d $(B)/missionpack/cgame ];then mkdir $(B)/missionpack/cgame;fi
+ @if [ ! -d $(B)/missionpack/game ];then mkdir $(B)/missionpack/game;fi
+ @if [ ! -d $(B)/missionpack/ui ];then mkdir $(B)/missionpack/ui;fi
+ @if [ ! -d $(B)/missionpack/vm ];then mkdir $(B)/missionpack/vm;fi
+ @if [ ! -d $(B)/q3static ];then mkdir $(B)/q3static;fi
+
+#############################################################################
+# CLIENT/SERVER
+#############################################################################
+
+Q3OBJ = \
+ $(B)/client/cl_cgame.o \
+ $(B)/client/cl_cin.o \
+ $(B)/client/cl_console.o \
+ $(B)/client/cl_input.o \
+ $(B)/client/cl_keys.o \
+ $(B)/client/cl_main.o \
+ $(B)/client/cl_net_chan.o \
+ $(B)/client/cl_parse.o \
+ $(B)/client/cl_scrn.o \
+ $(B)/client/cl_ui.o \
+ \
+ $(B)/client/cm_load.o \
+ $(B)/client/cm_patch.o \
+ $(B)/client/cm_polylib.o \
+ $(B)/client/cm_test.o \
+ $(B)/client/cm_trace.o \
+ \
+ $(B)/client/cmd.o \
+ $(B)/client/common.o \
+ $(B)/client/cvar.o \
+ $(B)/client/files.o \
+ $(B)/client/md4.o \
+ $(B)/client/msg.o \
+ $(B)/client/net_chan.o \
+ $(B)/client/huffman.o \
+ \
+ $(B)/client/snd_adpcm.o \
+ $(B)/client/snd_dma.o \
+ $(B)/client/snd_mem.o \
+ $(B)/client/snd_mix.o \
+ $(B)/client/snd_wavelet.o \
+ \
+ $(B)/client/sv_bot.o \
+ $(B)/client/sv_ccmds.o \
+ $(B)/client/sv_client.o \
+ $(B)/client/sv_game.o \
+ $(B)/client/sv_init.o \
+ $(B)/client/sv_main.o \
+ $(B)/client/sv_net_chan.o \
+ $(B)/client/sv_snapshot.o \
+ $(B)/client/sv_world.o \
+ \
+ $(B)/client/q_math.o \
+ $(B)/client/q_shared.o \
+ \
+ $(B)/client/unzip.o \
+ $(B)/client/vm.o \
+ $(B)/client/vm_interpreted.o \
+ \
+ $(B)/client/be_aas_bspq3.o \
+ $(B)/client/be_aas_cluster.o \
+ $(B)/client/be_aas_debug.o \
+ $(B)/client/be_aas_entity.o \
+ $(B)/client/be_aas_file.o \
+ $(B)/client/be_aas_main.o \
+ $(B)/client/be_aas_move.o \
+ $(B)/client/be_aas_optimize.o \
+ $(B)/client/be_aas_reach.o \
+ $(B)/client/be_aas_route.o \
+ $(B)/client/be_aas_routealt.o \
+ $(B)/client/be_aas_sample.o \
+ $(B)/client/be_ai_char.o \
+ $(B)/client/be_ai_chat.o \
+ $(B)/client/be_ai_gen.o \
+ $(B)/client/be_ai_goal.o \
+ $(B)/client/be_ai_move.o \
+ $(B)/client/be_ai_weap.o \
+ $(B)/client/be_ai_weight.o \
+ $(B)/client/be_ea.o \
+ $(B)/client/be_interface.o \
+ $(B)/client/l_crc.o \
+ $(B)/client/l_libvar.o \
+ $(B)/client/l_log.o \
+ $(B)/client/l_memory.o \
+ $(B)/client/l_precomp.o \
+ $(B)/client/l_script.o \
+ $(B)/client/l_struct.o \
+ \
+ $(B)/client/jcapimin.o \
+ $(B)/client/jchuff.o \
+ $(B)/client/jcinit.o \
+ $(B)/client/jccoefct.o \
+ $(B)/client/jccolor.o \
+ $(B)/client/jfdctflt.o \
+ $(B)/client/jcdctmgr.o \
+ $(B)/client/jcphuff.o \
+ $(B)/client/jcmainct.o \
+ $(B)/client/jcmarker.o \
+ $(B)/client/jcmaster.o \
+ $(B)/client/jcomapi.o \
+ $(B)/client/jcparam.o \
+ $(B)/client/jcprepct.o \
+ $(B)/client/jcsample.o \
+ $(B)/client/jdapimin.o \
+ $(B)/client/jdapistd.o \
+ $(B)/client/jdatasrc.o \
+ $(B)/client/jdcoefct.o \
+ $(B)/client/jdcolor.o \
+ $(B)/client/jddctmgr.o \
+ $(B)/client/jdhuff.o \
+ $(B)/client/jdinput.o \
+ $(B)/client/jdmainct.o \
+ $(B)/client/jdmarker.o \
+ $(B)/client/jdmaster.o \
+ $(B)/client/jdpostct.o \
+ $(B)/client/jdsample.o \
+ $(B)/client/jdtrans.o \
+ $(B)/client/jerror.o \
+ $(B)/client/jidctflt.o \
+ $(B)/client/jmemmgr.o \
+ $(B)/client/jmemnobs.o \
+ $(B)/client/jutils.o \
+ \
+ $(B)/client/tr_animation.o \
+ $(B)/client/tr_backend.o \
+ $(B)/client/tr_bsp.o \
+ $(B)/client/tr_cmds.o \
+ $(B)/client/tr_curve.o \
+ $(B)/client/tr_flares.o \
+ $(B)/client/tr_font.o \
+ $(B)/client/tr_image.o \
+ $(B)/client/tr_init.o \
+ $(B)/client/tr_light.o \
+ $(B)/client/tr_main.o \
+ $(B)/client/tr_marks.o \
+ $(B)/client/tr_mesh.o \
+ $(B)/client/tr_model.o \
+ $(B)/client/tr_noise.o \
+ $(B)/client/tr_scene.o \
+ $(B)/client/tr_shade.o \
+ $(B)/client/tr_shade_calc.o \
+ $(B)/client/tr_shader.o \
+ $(B)/client/tr_shadows.o \
+ $(B)/client/tr_sky.o \
+ $(B)/client/tr_surface.o \
+ $(B)/client/tr_world.o \
+ \
+ $(B)/client/unix_main.o \
+ $(B)/client/unix_net.o \
+ $(B)/client/unix_shared.o \
+ \
+ $(B)/client/ahoptim.o \
+ $(B)/client/autohint.o \
+ $(B)/client/ftbase.o \
+ $(B)/client/ftdebug.o \
+ $(B)/client/ftglyph.o \
+ $(B)/client/ftinit.o \
+ $(B)/client/ftmm.o \
+ $(B)/client/ftsystem.o \
+ $(B)/client/raster1.o \
+ $(B)/client/sfnt.o \
+ $(B)/client/sfobjs.o \
+ $(B)/client/smooth.o \
+ $(B)/client/truetype.o
+# \
+# $(B)/client/q_parse.o \
+# $(B)/client/math_quaternion.o \
+# $(B)/client/util_str.o \
+# $(B)/client/math_angles.o \
+# $(B)/client/math_vector.o \
+# $(B)/client/splines.o \
+# $(B)/client/math_matrix.o
+
+# $(B)/client/q_shared.o \
+
+ ifeq ($(ARCH),i386)
+ Q3OBJ += $(B)/client/vm_x86.o
+ endif
+
+ ifeq ($(ARCH),ppc)
+ ifeq ($(DLL_ONLY),false)
+ Q3OBJ += $(B)/client/vm_ppc.o
+ endif
+ endif
+
+# $(B)/client/jctrans.o \
+
+#platform specific objects
+ifeq ($(PLATFORM),freebsd)
+ Q3POBJ=\
+ $(B)/client/linux_common.o \
+ $(B)/client/linux_qgl.o \
+ $(B)/client/linux_glimp.o \
+ $(B)/client/linux_snd.o \
+ $(B)/client/snd_mixa.o \
+ $(B)/client/matha.o \
+ $(B)/client/ftol.o \
+ $(B)/client/snapvector.o
+else
+ifeq ($(PLATFORM),irix)
+ Q3POBJ=\
+ $(B)/client/irix_qgl.o \
+ $(B)/client/irix_glimp.o \
+ $(B)/client/irix_snd.o
+else
+ifeq ($(PLATFORM),linux)
+ifeq ($(ARCH),axp)
+ Q3POBJ=
+else
+ Q3POBJ=\
+ $(B)/client/linux_common.o \
+ $(B)/client/linux_qgl.o \
+ $(B)/client/linux_glimp.o \
+ $(B)/client/linux_joystick.o \
+ $(B)/client/linux_snd.o \
+ $(B)/client/snd_mixa.o \
+ $(B)/client/matha.o \
+
+ Q3POBJ_SMP=\
+ $(B)/client/linux_common.o \
+ $(B)/client/linux_qgl.o \
+ $(B)/client/linux_glimp_smp.o \
+ $(B)/client/linux_joystick.o \
+ $(B)/client/linux_snd.o \
+ $(B)/client/snd_mixa.o \
+ $(B)/client/matha.o
+
+ ifeq ($(ARCH),i386)
+ Q3POBJ += $(B)/client/ftol.o $(B)/client/snapvector.o
+ Q3POBJ_SMP += $(B)/client/ftol.o $(B)/client/snapvector.o
+ endif
+
+endif
+endif #Linux
+endif #FreeBSD
+endif #IRIX
+
+$(B)/$(PLATFORM)quake3 : $(Q3OBJ) $(Q3POBJ)
+ $(CC) -o $@ $(Q3OBJ) $(Q3POBJ) $(GLLDFLAGS) $(LDFLAGS)
+# TTimo: splines code requires C++ linking, but splines have not been officially included in the codebase
+# $(CXX) -o $@ $(Q3OBJ) $(Q3POBJ) $(GLLDFLAGS) $(LDFLAGS)
+
+$(B)/$(PLATFORM)quake3-smp : $(Q3OBJ) $(Q3POBJ_SMP)
+ $(CC) -o $@ $(Q3OBJ) $(Q3POBJ_SMP) $(GLLDFLAGS) \
+ $(THREAD_LDFLAGS) $(LDFLAGS)
+
+$(B)/client/cl_cgame.o : $(CDIR)/cl_cgame.c; $(DO_CC)
+$(B)/client/cl_cin.o : $(CDIR)/cl_cin.c; $(DO_CC)
+$(B)/client/cl_console.o : $(CDIR)/cl_console.c; $(DO_CC)
+$(B)/client/cl_input.o : $(CDIR)/cl_input.c; $(DO_CC)
+$(B)/client/cl_keys.o : $(CDIR)/cl_keys.c; $(DO_CC)
+$(B)/client/cl_main.o : $(CDIR)/cl_main.c; $(DO_CC)
+$(B)/client/cl_net_chan.o : $(CDIR)/cl_net_chan.c; $(DO_CC)
+$(B)/client/cl_parse.o : $(CDIR)/cl_parse.c; $(DO_CC)
+$(B)/client/cl_scrn.o : $(CDIR)/cl_scrn.c; $(DO_CC)
+$(B)/client/cl_ui.o : $(CDIR)/cl_ui.c; $(DO_CC)
+$(B)/client/snd_adpcm.o : $(CDIR)/snd_adpcm.c; $(DO_CC)
+$(B)/client/snd_dma.o : $(CDIR)/snd_dma.c; $(DO_CC)
+$(B)/client/snd_mem.o : $(CDIR)/snd_mem.c; $(DO_CC)
+$(B)/client/snd_mix.o : $(CDIR)/snd_mix.c; $(DO_CC)
+$(B)/client/snd_wavelet.o : $(CDIR)/snd_wavelet.c; $(DO_CC)
+$(B)/client/sv_bot.o : $(SDIR)/sv_bot.c; $(DO_CC)
+$(B)/client/sv_client.o : $(SDIR)/sv_client.c; $(DO_CC)
+$(B)/client/sv_ccmds.o : $(SDIR)/sv_ccmds.c; $(DO_CC)
+$(B)/client/sv_game.o : $(SDIR)/sv_game.c; $(DO_CC)
+$(B)/client/sv_init.o : $(SDIR)/sv_init.c; $(DO_CC)
+$(B)/client/sv_main.o : $(SDIR)/sv_main.c; $(DO_CC)
+$(B)/client/sv_net_chan.o : $(SDIR)/sv_net_chan.c; $(DO_CC)
+$(B)/client/sv_snapshot.o : $(SDIR)/sv_snapshot.c; $(DO_CC)
+$(B)/client/sv_world.o : $(SDIR)/sv_world.c; $(DO_CC)
+$(B)/client/cm_trace.o : $(CMDIR)/cm_trace.c; $(DO_CC)
+$(B)/client/cm_load.o : $(CMDIR)/cm_load.c; $(DO_CC)
+$(B)/client/cm_test.o : $(CMDIR)/cm_test.c; $(DO_CC)
+$(B)/client/cm_patch.o : $(CMDIR)/cm_patch.c; $(DO_CC)
+$(B)/client/cm_polylib.o : $(CMDIR)/cm_polylib.c; $(DO_CC)
+$(B)/client/cmd.o : $(CMDIR)/cmd.c; $(DO_CC)
+$(B)/client/common.o : $(CMDIR)/common.c; $(DO_CC)
+$(B)/client/cvar.o : $(CMDIR)/cvar.c; $(DO_CC)
+$(B)/client/files.o : $(CMDIR)/files.c; $(DO_CC)
+$(B)/client/md4.o : $(CMDIR)/md4.c; $(DO_CC)
+$(B)/client/msg.o : $(CMDIR)/msg.c; $(DO_CC)
+$(B)/client/net_chan.o : $(CMDIR)/net_chan.c; $(DO_CC)
+$(B)/client/huffman.o : $(CMDIR)/huffman.c; $(DO_CC)
+$(B)/client/q_shared.o : $(GDIR)/q_shared.c; $(DO_CC)
+$(B)/client/q_math.o : $(GDIR)/q_math.c; $(DO_CC)
+
+$(B)/client/be_aas_bspq3.o : $(BLIBDIR)/be_aas_bspq3.c; $(DO_BOT_CC)
+$(B)/client/be_aas_cluster.o : $(BLIBDIR)/be_aas_cluster.c; $(DO_BOT_CC)
+$(B)/client/be_aas_debug.o : $(BLIBDIR)/be_aas_debug.c; $(DO_BOT_CC)
+$(B)/client/be_aas_entity.o : $(BLIBDIR)/be_aas_entity.c; $(DO_BOT_CC)
+$(B)/client/be_aas_file.o : $(BLIBDIR)/be_aas_file.c; $(DO_BOT_CC)
+$(B)/client/be_aas_main.o : $(BLIBDIR)/be_aas_main.c; $(DO_BOT_CC)
+$(B)/client/be_aas_move.o : $(BLIBDIR)/be_aas_move.c; $(DO_BOT_CC)
+$(B)/client/be_aas_optimize.o : $(BLIBDIR)/be_aas_optimize.c; $(DO_BOT_CC)
+$(B)/client/be_aas_reach.o : $(BLIBDIR)/be_aas_reach.c; $(DO_BOT_CC)
+$(B)/client/be_aas_route.o : $(BLIBDIR)/be_aas_route.c; $(DO_BOT_CC)
+$(B)/client/be_aas_routealt.o : $(BLIBDIR)/be_aas_routealt.c; $(DO_BOT_CC)
+$(B)/client/be_aas_sample.o : $(BLIBDIR)/be_aas_sample.c; $(DO_BOT_CC)
+$(B)/client/be_ai_char.o : $(BLIBDIR)/be_ai_char.c; $(DO_BOT_CC)
+$(B)/client/be_ai_chat.o : $(BLIBDIR)/be_ai_chat.c; $(DO_BOT_CC)
+$(B)/client/be_ai_gen.o : $(BLIBDIR)/be_ai_gen.c; $(DO_BOT_CC)
+$(B)/client/be_ai_goal.o : $(BLIBDIR)/be_ai_goal.c; $(DO_BOT_CC)
+$(B)/client/be_ai_move.o : $(BLIBDIR)/be_ai_move.c; $(DO_BOT_CC)
+$(B)/client/be_ai_weap.o : $(BLIBDIR)/be_ai_weap.c; $(DO_BOT_CC)
+$(B)/client/be_ai_weight.o : $(BLIBDIR)/be_ai_weight.c; $(DO_BOT_CC)
+$(B)/client/be_ea.o : $(BLIBDIR)/be_ea.c; $(DO_BOT_CC)
+$(B)/client/be_interface.o : $(BLIBDIR)/be_interface.c; $(DO_BOT_CC)
+$(B)/client/l_crc.o : $(BLIBDIR)/l_crc.c; $(DO_BOT_CC)
+$(B)/client/l_libvar.o : $(BLIBDIR)/l_libvar.c; $(DO_BOT_CC)
+$(B)/client/l_log.o : $(BLIBDIR)/l_log.c; $(DO_BOT_CC)
+$(B)/client/l_memory.o : $(BLIBDIR)/l_memory.c; $(DO_BOT_CC)
+$(B)/client/l_precomp.o : $(BLIBDIR)/l_precomp.c; $(DO_BOT_CC)
+$(B)/client/l_script.o : $(BLIBDIR)/l_script.c; $(DO_BOT_CC)
+$(B)/client/l_struct.o : $(BLIBDIR)/l_struct.c; $(DO_BOT_CC)
+
+$(B)/client/jcapimin.o : $(JPDIR)/jcapimin.c; $(DO_CC)
+$(B)/client/jchuff.o : $(JPDIR)/jchuff.c; $(DO_CC)
+$(B)/client/jcinit.o : $(JPDIR)/jcinit.c; $(DO_CC)
+$(B)/client/jccoefct.o : $(JPDIR)/jccoefct.c; $(DO_CC)
+$(B)/client/jccolor.o : $(JPDIR)/jccolor.c; $(DO_CC)
+$(B)/client/jfdctflt.o : $(JPDIR)/jfdctflt.c; $(DO_CC)
+$(B)/client/jcdctmgr.o : $(JPDIR)/jcdctmgr.c; $(DO_CC)
+$(B)/client/jcmainct.o : $(JPDIR)/jcmainct.c; $(DO_CC)
+$(B)/client/jcmarker.o : $(JPDIR)/jcmarker.c; $(DO_CC)
+$(B)/client/jcmaster.o : $(JPDIR)/jcmaster.c; $(DO_CC)
+$(B)/client/jcomapi.o : $(JPDIR)/jcomapi.c; $(DO_CC)
+$(B)/client/jcparam.o : $(JPDIR)/jcparam.c; $(DO_CC)
+$(B)/client/jcprepct.o : $(JPDIR)/jcprepct.c; $(DO_CC)
+$(B)/client/jcsample.o : $(JPDIR)/jcsample.c; $(DO_CC)
+
+$(B)/client/jdapimin.o : $(JPDIR)/jdapimin.c; $(DO_CC)
+$(B)/client/jdapistd.o : $(JPDIR)/jdapistd.c; $(DO_CC)
+$(B)/client/jdatasrc.o : $(JPDIR)/jdatasrc.c; $(DO_CC)
+$(B)/client/jdcoefct.o : $(JPDIR)/jdcoefct.c; $(DO_CC)
+$(B)/client/jdcolor.o : $(JPDIR)/jdcolor.c; $(DO_CC)
+$(B)/client/jcphuff.o : $(JPDIR)/jcphuff.c; $(DO_CC)
+$(B)/client/jddctmgr.o : $(JPDIR)/jddctmgr.c; $(DO_CC)
+$(B)/client/jdhuff.o : $(JPDIR)/jdhuff.c; $(DO_CC)
+$(B)/client/jdinput.o : $(JPDIR)/jdinput.c; $(DO_CC)
+$(B)/client/jdmainct.o : $(JPDIR)/jdmainct.c; $(DO_CC)
+$(B)/client/jdmarker.o : $(JPDIR)/jdmarker.c; $(DO_CC)
+$(B)/client/jdmaster.o : $(JPDIR)/jdmaster.c; $(DO_CC)
+$(B)/client/jdpostct.o : $(JPDIR)/jdpostct.c; $(DO_CC)
+$(B)/client/jdsample.o : $(JPDIR)/jdsample.c; $(DO_CC)
+$(B)/client/jdtrans.o : $(JPDIR)/jdtrans.c; $(DO_CC)
+$(B)/client/jerror.o : $(JPDIR)/jerror.c; $(DO_CC) $(GL_CFLAGS)
+$(B)/client/jidctflt.o : $(JPDIR)/jidctflt.c; $(DO_CC)
+$(B)/client/jmemmgr.o : $(JPDIR)/jmemmgr.c; $(DO_CC)
+$(B)/client/jmemnobs.o : $(JPDIR)/jmemnobs.c; $(DO_CC) $(GL_CFLAGS)
+$(B)/client/jutils.o : $(JPDIR)/jutils.c; $(DO_CC)
+
+$(B)/client/tr_bsp.o : $(RDIR)/tr_bsp.c; $(DO_CC) $(GL_CFLAGS)
+$(B)/client/tr_animation.o : $(RDIR)/tr_animation.c; $(DO_CC) $(GL_CFLAGS)
+$(B)/client/tr_backend.o : $(RDIR)/tr_backend.c; $(DO_CC) $(GL_CFLAGS)
+$(B)/client/tr_cmds.o : $(RDIR)/tr_cmds.c; $(DO_CC) $(GL_CFLAGS)
+$(B)/client/tr_curve.o : $(RDIR)/tr_curve.c; $(DO_CC) $(GL_CFLAGS)
+$(B)/client/tr_flares.o : $(RDIR)/tr_flares.c; $(DO_CC) $(GL_CFLAGS)
+$(B)/client/tr_font.o : $(RDIR)/tr_font.c; $(DO_CC) $(GL_CFLAGS)
+$(B)/client/tr_image.o : $(RDIR)/tr_image.c; $(DO_CC) $(GL_CFLAGS)
+$(B)/client/tr_init.o : $(RDIR)/tr_init.c; $(DO_CC) $(GL_CFLAGS)
+$(B)/client/tr_light.o : $(RDIR)/tr_light.c; $(DO_CC) $(GL_CFLAGS)
+$(B)/client/tr_main.o : $(RDIR)/tr_main.c; $(DO_CC) $(GL_CFLAGS)
+$(B)/client/tr_marks.o : $(RDIR)/tr_marks.c; $(DO_CC) $(GL_CFLAGS)
+$(B)/client/tr_mesh.o : $(RDIR)/tr_mesh.c; $(DO_CC) $(GL_CFLAGS)
+$(B)/client/tr_model.o : $(RDIR)/tr_model.c; $(DO_CC) $(GL_CFLAGS)
+$(B)/client/tr_noise.o : $(RDIR)/tr_noise.c; $(DO_CC) $(GL_CFLAGS)
+$(B)/client/tr_scene.o : $(RDIR)/tr_scene.c; $(DO_CC) $(GL_CFLAGS)
+$(B)/client/tr_shade.o : $(RDIR)/tr_shade.c; $(DO_CC) $(GL_CFLAGS)
+$(B)/client/tr_shader.o : $(RDIR)/tr_shader.c; $(DO_CC) $(GL_CFLAGS)
+$(B)/client/tr_shade_calc.o : $(RDIR)/tr_shade_calc.c; $(DO_CC) $(GL_CFLAGS)
+$(B)/client/tr_shadows.o : $(RDIR)/tr_shadows.c; $(DO_CC) $(GL_CFLAGS)
+$(B)/client/tr_sky.o : $(RDIR)/tr_sky.c; $(DO_CC) $(GL_CFLAGS)
+$(B)/client/tr_smp.o : $(RDIR)/tr_smp.c; $(DO_CC) $(GL_CFLAGS)
+$(B)/client/tr_stripify.o : $(RDIR)/tr_stripify.c; $(DO_CC) $(GL_CFLAGS)
+$(B)/client/tr_subdivide.o : $(RDIR)/tr_subdivide.c; $(DO_CC) $(GL_CFLAGS)
+$(B)/client/tr_surface.o : $(RDIR)/tr_surface.c; $(DO_CC) $(GL_CFLAGS)
+$(B)/client/tr_world.o : $(RDIR)/tr_world.c; $(DO_CC) $(GL_CFLAGS)
+
+$(B)/client/unix_qgl.o : $(UDIR)/unix_qgl.c; $(DO_CC) $(GL_CFLAGS)
+$(B)/client/unix_main.o : $(UDIR)/unix_main.c; $(DO_CC)
+$(B)/client/unix_net.o : $(UDIR)/unix_net.c; $(DO_CC)
+$(B)/client/unix_shared.o : $(UDIR)/unix_shared.c; $(DO_CC)
+$(B)/client/irix_glimp.o : $(UDIR)/irix_glimp.c; $(DO_CC)
+$(B)/client/irix_glimp_smp.o : $(UDIR)/irix_glimp.c; $(DO_SMP_CC)
+$(B)/client/irix_snd.o : $(UDIR)/irix_snd.c; $(DO_CC)
+$(B)/client/irix_input.o : $(UDIR)/irix_input.c; $(DO_CC)
+$(B)/client/linux_common.o : $(UDIR)/linux_common.c; $(DO_CC)
+$(B)/client/linux_glimp.o : $(UDIR)/linux_glimp.c; $(DO_CC) $(GL_CFLAGS)
+$(B)/client/linux_glimp_smp.o : $(UDIR)/linux_glimp.c; $(DO_SMP_CC) $(GL_CFLAGS)
+$(B)/client/linux_joystick.o : $(UDIR)/linux_joystick.c; $(DO_CC)
+$(B)/client/linux_qgl.o : $(UDIR)/linux_qgl.c; $(DO_CC) $(GL_CFLAGS)
+$(B)/client/linux_input.o : $(UDIR)/linux_input.c; $(DO_CC)
+$(B)/client/linux_snd.o : $(UDIR)/linux_snd.c; $(DO_CC)
+$(B)/client/snd_mixa.o : $(UDIR)/snd_mixa.s; $(DO_AS)
+$(B)/client/matha.o : $(UDIR)/matha.s; $(DO_AS)
+
+ifeq ($(ARCH),i386)
+$(B)/client/ftol.o : $(UDIR)/ftol.nasm; $(DO_NASM)
+$(B)/client/snapvector.o : $(UDIR)/snapvector.nasm; $(DO_NASM)
+$(B)/client/vm_x86.o : $(CMDIR)/vm_x86.c; $(DO_CC)
+endif
+
+ifeq ($(ARCH),ppc)
+ifeq ($(DLL_ONLY),false)
+$(B)/client/vm_ppc.o : $(CMDIR)/vm_ppc.c; $(DO_CC)
+endif
+endif
+
+$(B)/client/unzip.o : $(CMDIR)/unzip.c; $(DO_CC)
+$(B)/client/vm.o : $(CMDIR)/vm.c; $(DO_CC)
+$(B)/client/vm_interpreted.o : $(CMDIR)/vm_interpreted.c; $(DO_CC)
+
+$(B)/client/ahoptim.o : $(FTDIR)/ahoptim.c; $(DO_CC)
+$(B)/client/autohint.o : $(FTDIR)/autohint.c; $(DO_CC)
+$(B)/client/ftbase.o : $(FTDIR)/ftbase.c; $(DO_CC)
+$(B)/client/ftdebug.o : $(FTDIR)/ftdebug.c; $(DO_CC)
+$(B)/client/ftglyph.o : $(FTDIR)/ftglyph.c; $(DO_CC)
+$(B)/client/ftinit.o : $(FTDIR)/ftinit.c; $(DO_CC)
+$(B)/client/ftmm.o : $(FTDIR)/ftmm.c; $(DO_CC)
+$(B)/client/ftsystem.o : $(FTDIR)/ftsystem.c; $(DO_CC)
+$(B)/client/raster1.o : $(FTDIR)/raster1.c; $(DO_CC) -DFT_FLAT_COMPILE
+$(B)/client/sfnt.o : $(FTDIR)/sfnt.c; $(DO_CC)
+$(B)/client/sfobjs.o : $(FTDIR)/sfobjs.c; $(DO_CC)
+$(B)/client/smooth.o : $(FTDIR)/smooth.c; $(DO_CC) -DFT_FLAT_COMPILE
+$(B)/client/truetype.o : $(FTDIR)/truetype.c; $(DO_CC)
+
+# TTimo: took out splines code
+#$(B)/client/q_parse.o : $(SPLNDIR)/q_parse.cpp; $(DO_CXX)
+#$(B)/client/math_quaternion.o : $(SPLNDIR)/math_quaternion.cpp; $(DO_CXX)
+# TTimo: ignored
+#$(B)/client/q_shared.o : $(SPLNDIR)/q_shared.cpp; $(DO_CXX)
+#$(B)/client/util_str.o : $(SPLNDIR)/util_str.cpp; $(DO_CXX)
+#$(B)/client/math_angles.o : $(SPLNDIR)/math_angles.cpp; $(DO_CXX)
+#$(B)/client/math_vector.o : $(SPLNDIR)/math_vector.cpp; $(DO_CXX)
+#$(B)/client/splines.o : $(SPLNDIR)/splines.cpp; $(DO_CXX)
+#$(B)/client/math_matrix.o : $(SPLNDIR)/math_matrix.cpp; $(DO_CXX)
+
+## Included by raster1.c
+# $(B)/client/ftraster.o : $(FTDIR)/ftraster.c; $(DO_CC)
+# $(B)/client/ftrend1.o : $(FTDIR)/ftrend1.c; $(DO_CC)
+
+## Included by autohint.c
+# $(B)/client/ahangles.o : $(FTDIR)/ahangles.c; $(DO_CC)
+# $(B)/client/ahglyph.o : $(FTDIR)/ahglyph.c; $(DO_CC)
+# $(B)/client/ahglobal.o : $(FTDIR)/ahglobal.c; $(DO_CC)
+# $(B)/client/ahhint.o : $(FTDIR)/ahhint.c; $(DO_CC)
+# $(B)/client/ahmodule.o : $(FTDIR)/ahmodule.c; $(DO_CC)
+
+## Included by ftcalc.c
+# $(B)/client/ftcalc.o : $(FTDIR)/ftcalc.c; $(DO_CC)
+# $(B)/client/ftobjs.o : $(FTDIR)/ftobjs.c; $(DO_CC)
+# $(B)/client/ftstream.o : $(FTDIR)/ftstream.c; $(DO_CC)
+# $(B)/client/ftlist.o : $(FTDIR)/ftlist.c; $(DO_CC)
+# $(B)/client/ftoutln.o : $(FTDIR)/ftoutln.c; $(DO_CC)
+# $(B)/client/ftextend.o : $(FTDIR)/ftextend.c; $(DO_CC)
+# $(B)/client/ftnames.o : $(FTDIR)/ftnames.c; $(DO_CC)
+
+## Included by sfnt.c
+# $(B)/client/ttload.o : $(FTDIR)/ttload.c; $(DO_CC)
+# $(B)/client/ttcmap.o : $(FTDIR)/ttcmap.c; $(DO_CC)
+# $(B)/client/sfobjs.o : $(FTDIR)/sfobjs.c; $(DO_CC)
+# $(B)/client/sfdriver.o : $(FTDIR)/sfdriver.c; $(DO_CC)
+## Unused in sfnt.c
+# $(B)/client/ttpost.o : $(FTDIR)/ttpost.c; $(DO_CC)
+# $(B)/client/ttsbit.o : $(FTDIR)/ttsbit.c; $(DO_CC)
+
+## Included by truetype.c
+# $(B)/client/ttdriver.o : $(FTDIR)/ttdriver.c; $(DO_CC)
+# $(B)/client/ttgload.o : $(FTDIR)/ttgload.c; $(DO_CC)
+# $(B)/client/ttobjs.o : $(FTDIR)/ttobjs.c; $(DO_CC)
+# $(B)/client/ttpload.o : $(FTDIR)/ttpload.c; $(DO_CC)
+## Unused in truetype.c
+# $(B)/client/ttinterp.o : $(FTDIR)/ttinterp.c; $(DO_CC)
+
+## Included by smooth.c
+# $(B)/client/ftsmooth.o : $(FTDIR)/ftsmooth.c; $(DO_CC) -DFT_FLAT_COMPILE
+# $(B)/client/ftgrays.o : $(FTDIR)/ftgrays.c; $(DO_CC)
+
+#############################################################################
+# DEDICATED SERVER
+#############################################################################
+
+Q3DOBJ = \
+ $(B)/ded/sv_bot.o \
+ $(B)/ded/sv_client.o \
+ $(B)/ded/sv_ccmds.o \
+ $(B)/ded/sv_game.o \
+ $(B)/ded/sv_init.o \
+ $(B)/ded/sv_main.o \
+ $(B)/ded/sv_net_chan.o \
+ $(B)/ded/sv_snapshot.o \
+ $(B)/ded/sv_world.o \
+ \
+ $(B)/ded/cm_load.o \
+ $(B)/ded/cm_patch.o \
+ $(B)/ded/cm_polylib.o \
+ $(B)/ded/cm_test.o \
+ $(B)/ded/cm_trace.o \
+ $(B)/ded/cmd.o \
+ $(B)/ded/common.o \
+ $(B)/ded/cvar.o \
+ $(B)/ded/files.o \
+ $(B)/ded/md4.o \
+ $(B)/ded/msg.o \
+ $(B)/ded/net_chan.o \
+ $(B)/ded/huffman.o \
+ \
+ $(B)/ded/q_math.o \
+ $(B)/ded/q_shared.o \
+ \
+ $(B)/ded/unzip.o \
+ $(B)/ded/vm.o \
+ $(B)/ded/vm_interpreted.o \
+ \
+ $(B)/ded/be_aas_bspq3.o \
+ $(B)/ded/be_aas_cluster.o \
+ $(B)/ded/be_aas_debug.o \
+ $(B)/ded/be_aas_entity.o \
+ $(B)/ded/be_aas_file.o \
+ $(B)/ded/be_aas_main.o \
+ $(B)/ded/be_aas_move.o \
+ $(B)/ded/be_aas_optimize.o \
+ $(B)/ded/be_aas_reach.o \
+ $(B)/ded/be_aas_route.o \
+ $(B)/ded/be_aas_routealt.o \
+ $(B)/ded/be_aas_sample.o \
+ $(B)/ded/be_ai_char.o \
+ $(B)/ded/be_ai_chat.o \
+ $(B)/ded/be_ai_gen.o \
+ $(B)/ded/be_ai_goal.o \
+ $(B)/ded/be_ai_move.o \
+ $(B)/ded/be_ai_weap.o \
+ $(B)/ded/be_ai_weight.o \
+ $(B)/ded/be_ea.o \
+ $(B)/ded/be_interface.o \
+ $(B)/ded/l_crc.o \
+ $(B)/ded/l_libvar.o \
+ $(B)/ded/l_log.o \
+ $(B)/ded/l_memory.o \
+ $(B)/ded/l_precomp.o \
+ $(B)/ded/l_script.o \
+ $(B)/ded/l_struct.o \
+ \
+ $(B)/ded/linux_common.o \
+ $(B)/ded/unix_main.o \
+ $(B)/ded/unix_net.o \
+ $(B)/ded/unix_shared.o \
+ \
+ $(B)/ded/null_client.o \
+ $(B)/ded/null_input.o \
+ $(B)/ded/null_snddma.o
+
+ifeq ($(ARCH),i386)
+ Q3DOBJ += $(B)/ded/vm_x86.o $(B)/ded/ftol.o $(B)/ded/snapvector.o
+endif
+
+ifeq ($(ARCH),ppc)
+ ifeq ($(DLL_ONLY),false)
+ Q3DOBJ += $(B)/ded/vm_ppc.o
+ endif
+endif
+
+$(B)/$(PLATFORM)q3ded : $(Q3DOBJ)
+ $(CC) -o $@ $(Q3DOBJ) $(LDFLAGS)
+
+$(B)/ded/sv_bot.o : $(SDIR)/sv_bot.c; $(DO_DED_CC)
+$(B)/ded/sv_client.o : $(SDIR)/sv_client.c; $(DO_DED_CC)
+$(B)/ded/sv_ccmds.o : $(SDIR)/sv_ccmds.c; $(DO_DED_CC)
+$(B)/ded/sv_game.o : $(SDIR)/sv_game.c; $(DO_DED_CC)
+$(B)/ded/sv_init.o : $(SDIR)/sv_init.c; $(DO_DED_CC)
+$(B)/ded/sv_main.o : $(SDIR)/sv_main.c; $(DO_DED_CC)
+$(B)/ded/sv_net_chan.o : $(SDIR)/sv_net_chan.c; $(DO_DED_CC)
+$(B)/ded/sv_snapshot.o : $(SDIR)/sv_snapshot.c; $(DO_DED_CC)
+$(B)/ded/sv_world.o : $(SDIR)/sv_world.c; $(DO_DED_CC)
+$(B)/ded/cm_load.o : $(CMDIR)/cm_load.c; $(DO_DED_CC)
+$(B)/ded/cm_polylib.o : $(CMDIR)/cm_polylib.c; $(DO_DED_CC)
+$(B)/ded/cm_test.o : $(CMDIR)/cm_test.c; $(DO_DED_CC)
+$(B)/ded/cm_trace.o : $(CMDIR)/cm_trace.c; $(DO_DED_CC)
+$(B)/ded/cm_patch.o : $(CMDIR)/cm_patch.c; $(DO_DED_CC)
+$(B)/ded/cmd.o : $(CMDIR)/cmd.c; $(DO_DED_CC)
+$(B)/ded/common.o : $(CMDIR)/common.c; $(DO_DED_CC)
+$(B)/ded/cvar.o : $(CMDIR)/cvar.c; $(DO_DED_CC)
+$(B)/ded/files.o : $(CMDIR)/files.c; $(DO_DED_CC)
+$(B)/ded/md4.o : $(CMDIR)/md4.c; $(DO_DED_CC)
+$(B)/ded/msg.o : $(CMDIR)/msg.c; $(DO_DED_CC)
+$(B)/ded/net_chan.o : $(CMDIR)/net_chan.c; $(DO_DED_CC)
+$(B)/ded/huffman.o : $(CMDIR)/huffman.c; $(DO_DED_CC)
+$(B)/ded/q_shared.o : $(GDIR)/q_shared.c; $(DO_DED_CC)
+$(B)/ded/q_math.o : $(GDIR)/q_math.c; $(DO_DED_CC)
+
+$(B)/ded/be_aas_bspq3.o : $(BLIBDIR)/be_aas_bspq3.c; $(DO_BOT_CC)
+$(B)/ded/be_aas_cluster.o : $(BLIBDIR)/be_aas_cluster.c; $(DO_BOT_CC)
+$(B)/ded/be_aas_debug.o : $(BLIBDIR)/be_aas_debug.c; $(DO_BOT_CC)
+$(B)/ded/be_aas_entity.o : $(BLIBDIR)/be_aas_entity.c; $(DO_BOT_CC)
+$(B)/ded/be_aas_file.o : $(BLIBDIR)/be_aas_file.c; $(DO_BOT_CC)
+$(B)/ded/be_aas_main.o : $(BLIBDIR)/be_aas_main.c; $(DO_BOT_CC)
+$(B)/ded/be_aas_move.o : $(BLIBDIR)/be_aas_move.c; $(DO_BOT_CC)
+$(B)/ded/be_aas_optimize.o : $(BLIBDIR)/be_aas_optimize.c; $(DO_BOT_CC)
+$(B)/ded/be_aas_reach.o : $(BLIBDIR)/be_aas_reach.c; $(DO_BOT_CC)
+$(B)/ded/be_aas_route.o : $(BLIBDIR)/be_aas_route.c; $(DO_BOT_CC)
+$(B)/ded/be_aas_routealt.o : $(BLIBDIR)/be_aas_routealt.c; $(DO_BOT_CC)
+$(B)/ded/be_aas_sample.o : $(BLIBDIR)/be_aas_sample.c; $(DO_BOT_CC)
+$(B)/ded/be_ai_char.o : $(BLIBDIR)/be_ai_char.c; $(DO_BOT_CC)
+$(B)/ded/be_ai_chat.o : $(BLIBDIR)/be_ai_chat.c; $(DO_BOT_CC)
+$(B)/ded/be_ai_gen.o : $(BLIBDIR)/be_ai_gen.c; $(DO_BOT_CC)
+$(B)/ded/be_ai_goal.o : $(BLIBDIR)/be_ai_goal.c; $(DO_BOT_CC)
+$(B)/ded/be_ai_move.o : $(BLIBDIR)/be_ai_move.c; $(DO_BOT_CC)
+$(B)/ded/be_ai_weap.o : $(BLIBDIR)/be_ai_weap.c; $(DO_BOT_CC)
+$(B)/ded/be_ai_weight.o : $(BLIBDIR)/be_ai_weight.c; $(DO_BOT_CC)
+$(B)/ded/be_ea.o : $(BLIBDIR)/be_ea.c; $(DO_BOT_CC)
+$(B)/ded/be_interface.o : $(BLIBDIR)/be_interface.c; $(DO_BOT_CC)
+$(B)/ded/l_crc.o : $(BLIBDIR)/l_crc.c; $(DO_BOT_CC)
+$(B)/ded/l_libvar.o : $(BLIBDIR)/l_libvar.c; $(DO_BOT_CC)
+$(B)/ded/l_log.o : $(BLIBDIR)/l_log.c; $(DO_BOT_CC)
+$(B)/ded/l_memory.o : $(BLIBDIR)/l_memory.c; $(DO_BOT_CC)
+$(B)/ded/l_precomp.o : $(BLIBDIR)/l_precomp.c; $(DO_BOT_CC)
+$(B)/ded/l_script.o : $(BLIBDIR)/l_script.c; $(DO_BOT_CC)
+$(B)/ded/l_struct.o : $(BLIBDIR)/l_struct.c; $(DO_BOT_CC)
+
+$(B)/ded/linux_common.o : $(UDIR)/linux_common.c; $(DO_CC)
+$(B)/ded/unix_main.o : $(UDIR)/unix_main.c; $(DO_DED_CC)
+$(B)/ded/unix_net.o : $(UDIR)/unix_net.c; $(DO_DED_CC)
+$(B)/ded/unix_shared.o : $(UDIR)/unix_shared.c; $(DO_DED_CC)
+$(B)/ded/null_client.o : $(NDIR)/null_client.c; $(DO_DED_CC)
+$(B)/ded/null_input.o : $(NDIR)/null_input.c; $(DO_DED_CC)
+$(B)/ded/null_snddma.o : $(NDIR)/null_snddma.c; $(DO_DED_CC)
+$(B)/ded/unzip.o : $(CMDIR)/unzip.c; $(DO_DED_CC)
+$(B)/ded/vm.o : $(CMDIR)/vm.c; $(DO_DED_CC)
+$(B)/ded/vm_interpreted.o : $(CMDIR)/vm_interpreted.c; $(DO_DED_CC)
+
+ifeq ($(ARCH),i386)
+$(B)/ded/vm_x86.o : $(CMDIR)/vm_x86.c; $(DO_DED_CC)
+$(B)/ded/ftol.o : $(UDIR)/ftol.nasm; $(DO_NASM)
+$(B)/ded/snapvector.o : $(UDIR)/snapvector.nasm; $(DO_NASM)
+endif
+
+ifeq ($(ARCH),ppc)
+ifeq ($(DLL_ONLY),false)
+$(B)/ded/vm_ppc.o : $(CMDIR)/vm_ppc.c; $(DO_DED_CC)
+endif
+endif
+
+
+#############################################################################
+## QVM
+#############################################################################
+
+$(B)/baseq3/vm/cgame.qvm:
+ cd $(CGDIR) && ./cgame.sh
+ mv /tmp/quake3/baseq3/vm/cgame.qvm $@
+
+$(B)/baseq3/vm/ui.qvm:
+ cd $(Q3UIDIR) && ./q3_ui.sh
+ mv /tmp/quake3/baseq3/vm/ui.qvm $@
+
+$(B)/baseq3/vm/qagame.qvm:
+ cd $(GDIR) && ./game.sh
+ mv /tmp/quake3/baseq3/vm/qagame.qvm $@
+
+$(B)/missionpack/vm/cgame.qvm:
+ cd $(CGDIR) && ./cgame_ta.sh
+ mv /tmp/quake3/missionpack/vm/cgame.qvm $@
+
+$(B)/missionpack/vm/qagame.qvm:
+ cd $(GDIR) && ./game_ta.sh
+ mv /tmp/quake3/missionpack/vm/qagame.qvm $@
+
+$(B)/missionpack/vm/ui.qvm:
+ cd $(UIDIR) && ./ui.sh
+ mv /tmp/quake3/missionpack/vm/ui.qvm $@
+
+
+
+#############################################################################
+## BASEQ3 CGAME
+#############################################################################
+
+Q3CGOBJ = \
+ $(B)/baseq3/cgame/bg_misc.o \
+ $(B)/baseq3/cgame/bg_pmove.o \
+ $(B)/baseq3/cgame/bg_slidemove.o \
+ $(B)/baseq3/cgame/cg_consolecmds.o \
+ $(B)/baseq3/cgame/cg_draw.o \
+ $(B)/baseq3/cgame/cg_drawtools.o \
+ $(B)/baseq3/cgame/cg_effects.o \
+ $(B)/baseq3/cgame/cg_ents.o \
+ $(B)/baseq3/cgame/cg_event.o \
+ $(B)/baseq3/cgame/cg_info.o \
+ $(B)/baseq3/cgame/cg_localents.o \
+ $(B)/baseq3/cgame/cg_main.o \
+ $(B)/baseq3/cgame/cg_marks.o \
+ $(B)/baseq3/cgame/cg_players.o \
+ $(B)/baseq3/cgame/cg_playerstate.o \
+ $(B)/baseq3/cgame/cg_predict.o \
+ $(B)/baseq3/cgame/cg_scoreboard.o \
+ $(B)/baseq3/cgame/cg_servercmds.o \
+ $(B)/baseq3/cgame/cg_snapshot.o \
+ $(B)/baseq3/cgame/cg_syscalls.o \
+ $(B)/baseq3/cgame/cg_view.o \
+ $(B)/baseq3/cgame/cg_weapons.o \
+ $(B)/baseq3/cgame/q_math.o \
+ $(B)/baseq3/cgame/q_shared.o
+
+$(B)/baseq3/cgame$(ARCH).$(SHLIBEXT) : $(Q3CGOBJ)
+ $(CC) $(SHLIBLDFLAGS) -o $@ $(Q3CGOBJ)
+
+$(B)/baseq3/cgame/bg_misc.o : $(GDIR)/bg_misc.c; $(DO_SHLIB_CC)
+$(B)/baseq3/cgame/bg_pmove.o : $(GDIR)/bg_pmove.c; $(DO_SHLIB_CC)
+$(B)/baseq3/cgame/bg_slidemove.o : $(GDIR)/bg_slidemove.c; $(DO_SHLIB_CC)
+$(B)/baseq3/cgame/cg_consolecmds.o : $(CGDIR)/cg_consolecmds.c; $(DO_SHLIB_CC)
+$(B)/baseq3/cgame/cg_draw.o : $(CGDIR)/cg_draw.c; $(DO_SHLIB_CC)
+$(B)/baseq3/cgame/cg_drawtools.o : $(CGDIR)/cg_drawtools.c; $(DO_SHLIB_CC)
+$(B)/baseq3/cgame/cg_effects.o : $(CGDIR)/cg_effects.c; $(DO_SHLIB_CC)
+$(B)/baseq3/cgame/cg_ents.o : $(CGDIR)/cg_ents.c; $(DO_SHLIB_CC)
+$(B)/baseq3/cgame/cg_event.o : $(CGDIR)/cg_event.c; $(DO_SHLIB_CC)
+$(B)/baseq3/cgame/cg_info.o : $(CGDIR)/cg_info.c; $(DO_SHLIB_CC)
+$(B)/baseq3/cgame/cg_localents.o : $(CGDIR)/cg_localents.c; $(DO_SHLIB_CC)
+$(B)/baseq3/cgame/cg_main.o : $(CGDIR)/cg_main.c; $(DO_SHLIB_CC)
+$(B)/baseq3/cgame/cg_marks.o : $(CGDIR)/cg_marks.c; $(DO_SHLIB_CC)
+$(B)/baseq3/cgame/cg_players.o : $(CGDIR)/cg_players.c; $(DO_SHLIB_CC)
+$(B)/baseq3/cgame/cg_playerstate.o : $(CGDIR)/cg_playerstate.c; $(DO_SHLIB_CC)
+$(B)/baseq3/cgame/cg_predict.o : $(CGDIR)/cg_predict.c; $(DO_SHLIB_CC)
+$(B)/baseq3/cgame/cg_scoreboard.o : $(CGDIR)/cg_scoreboard.c; $(DO_SHLIB_CC)
+$(B)/baseq3/cgame/cg_servercmds.o : $(CGDIR)/cg_servercmds.c; $(DO_SHLIB_CC)
+$(B)/baseq3/cgame/cg_snapshot.o : $(CGDIR)/cg_snapshot.c; $(DO_SHLIB_CC)
+$(B)/baseq3/cgame/cg_syscalls.o : $(CGDIR)/cg_syscalls.c; $(DO_SHLIB_CC)
+$(B)/baseq3/cgame/cg_view.o : $(CGDIR)/cg_view.c; $(DO_SHLIB_CC)
+$(B)/baseq3/cgame/cg_weapons.o : $(CGDIR)/cg_weapons.c; $(DO_SHLIB_CC)
+$(B)/baseq3/cgame/q_math.o : $(GDIR)/q_math.c; $(DO_SHLIB_CC)
+$(B)/baseq3/cgame/q_shared.o : $(GDIR)/q_shared.c; $(DO_SHLIB_CC)
+
+#############################################################################
+## MISSIONPACK CGAME
+#############################################################################
+
+MPCGOBJ = \
+ $(B)/missionpack/cgame/bg_misc.o \
+ $(B)/missionpack/cgame/bg_pmove.o \
+ $(B)/missionpack/cgame/bg_slidemove.o \
+ $(B)/missionpack/cgame/cg_consolecmds.o \
+ $(B)/missionpack/cgame/cg_newdraw.o \
+ $(B)/missionpack/cgame/cg_draw.o \
+ $(B)/missionpack/cgame/cg_drawtools.o \
+ $(B)/missionpack/cgame/cg_effects.o \
+ $(B)/missionpack/cgame/cg_ents.o \
+ $(B)/missionpack/cgame/cg_event.o \
+ $(B)/missionpack/cgame/cg_info.o \
+ $(B)/missionpack/cgame/cg_localents.o \
+ $(B)/missionpack/cgame/cg_main.o \
+ $(B)/missionpack/cgame/cg_marks.o \
+ $(B)/missionpack/cgame/cg_players.o \
+ $(B)/missionpack/cgame/cg_playerstate.o \
+ $(B)/missionpack/cgame/cg_predict.o \
+ $(B)/missionpack/cgame/cg_scoreboard.o \
+ $(B)/missionpack/cgame/cg_servercmds.o \
+ $(B)/missionpack/cgame/cg_snapshot.o \
+ $(B)/missionpack/cgame/cg_syscalls.o \
+ $(B)/missionpack/cgame/cg_view.o \
+ $(B)/missionpack/cgame/cg_weapons.o \
+ $(B)/missionpack/cgame/q_math.o \
+ $(B)/missionpack/cgame/q_shared.o \
+ $(B)/missionpack/cgame/ui_shared.o
+
+$(B)/missionpack/cgame$(ARCH).$(SHLIBEXT) : $(MPCGOBJ)
+ $(CC) $(SHLIBLDFLAGS) -o $@ $(MPCGOBJ)
+
+$(B)/missionpack/cgame/bg_misc.o : $(GDIR)/bg_misc.c; $(DO_SHLIB_CC) -DMISSIONPACK
+$(B)/missionpack/cgame/bg_pmove.o : $(GDIR)/bg_pmove.c; $(DO_SHLIB_CC) -DMISSIONPACK
+$(B)/missionpack/cgame/bg_slidemove.o : $(GDIR)/bg_slidemove.c; $(DO_SHLIB_CC) -DMISSIONPACK
+$(B)/missionpack/cgame/cg_consolecmds.o : $(CGDIR)/cg_consolecmds.c; $(DO_SHLIB_CC) -DMISSIONPACK
+$(B)/missionpack/cgame/cg_newdraw.o : $(CGDIR)/cg_newdraw.c; $(DO_SHLIB_CC) -DMISSIONPACK
+$(B)/missionpack/cgame/cg_draw.o : $(CGDIR)/cg_draw.c; $(DO_SHLIB_CC) -DMISSIONPACK
+$(B)/missionpack/cgame/cg_drawtools.o : $(CGDIR)/cg_drawtools.c; $(DO_SHLIB_CC) -DMISSIONPACK
+$(B)/missionpack/cgame/cg_effects.o : $(CGDIR)/cg_effects.c; $(DO_SHLIB_CC) -DMISSIONPACK
+$(B)/missionpack/cgame/cg_ents.o : $(CGDIR)/cg_ents.c; $(DO_SHLIB_CC) -DMISSIONPACK
+$(B)/missionpack/cgame/cg_event.o : $(CGDIR)/cg_event.c; $(DO_SHLIB_CC) -DMISSIONPACK
+$(B)/missionpack/cgame/cg_info.o : $(CGDIR)/cg_info.c; $(DO_SHLIB_CC) -DMISSIONPACK
+$(B)/missionpack/cgame/cg_localents.o : $(CGDIR)/cg_localents.c; $(DO_SHLIB_CC) -DMISSIONPACK
+$(B)/missionpack/cgame/cg_main.o : $(CGDIR)/cg_main.c; $(DO_SHLIB_CC) -DMISSIONPACK
+$(B)/missionpack/cgame/cg_marks.o : $(CGDIR)/cg_marks.c; $(DO_SHLIB_CC) -DMISSIONPACK
+$(B)/missionpack/cgame/cg_players.o : $(CGDIR)/cg_players.c; $(DO_SHLIB_CC) -DMISSIONPACK
+$(B)/missionpack/cgame/cg_playerstate.o : $(CGDIR)/cg_playerstate.c; $(DO_SHLIB_CC) -DMISSIONPACK
+$(B)/missionpack/cgame/cg_predict.o : $(CGDIR)/cg_predict.c; $(DO_SHLIB_CC) -DMISSIONPACK
+$(B)/missionpack/cgame/cg_scoreboard.o : $(CGDIR)/cg_scoreboard.c; $(DO_SHLIB_CC) -DMISSIONPACK
+$(B)/missionpack/cgame/cg_servercmds.o : $(CGDIR)/cg_servercmds.c; $(DO_SHLIB_CC) -DMISSIONPACK
+$(B)/missionpack/cgame/cg_snapshot.o : $(CGDIR)/cg_snapshot.c; $(DO_SHLIB_CC) -DMISSIONPACK
+$(B)/missionpack/cgame/cg_syscalls.o : $(CGDIR)/cg_syscalls.c; $(DO_SHLIB_CC) -DMISSIONPACK
+$(B)/missionpack/cgame/cg_view.o : $(CGDIR)/cg_view.c; $(DO_SHLIB_CC) -DMISSIONPACK
+$(B)/missionpack/cgame/cg_weapons.o : $(CGDIR)/cg_weapons.c; $(DO_SHLIB_CC) -DMISSIONPACK
+$(B)/missionpack/cgame/q_math.o : $(GDIR)/q_math.c; $(DO_SHLIB_CC) -DMISSIONPACK
+$(B)/missionpack/cgame/q_shared.o : $(GDIR)/q_shared.c; $(DO_SHLIB_CC) -DMISSIONPACK
+$(B)/missionpack/cgame/ui_shared.o : $(UIDIR)/ui_shared.c; $(DO_SHLIB_CC) -DMISSIONPACK
+
+
+
+#############################################################################
+## BASEQ3 GAME
+#############################################################################
+
+Q3GOBJ = \
+ $(B)/baseq3/game/ai_chat.o \
+ $(B)/baseq3/game/ai_cmd.o \
+ $(B)/baseq3/game/ai_dmnet.o \
+ $(B)/baseq3/game/ai_dmq3.o \
+ $(B)/baseq3/game/ai_main.o \
+ $(B)/baseq3/game/ai_team.o \
+ $(B)/baseq3/game/ai_vcmd.o \
+ $(B)/baseq3/game/bg_misc.o \
+ $(B)/baseq3/game/bg_pmove.o \
+ $(B)/baseq3/game/bg_slidemove.o \
+ $(B)/baseq3/game/g_active.o \
+ $(B)/baseq3/game/g_arenas.o \
+ $(B)/baseq3/game/g_bot.o \
+ $(B)/baseq3/game/g_client.o \
+ $(B)/baseq3/game/g_cmds.o \
+ $(B)/baseq3/game/g_combat.o \
+ $(B)/baseq3/game/g_items.o \
+ $(B)/baseq3/game/g_main.o \
+ $(B)/baseq3/game/g_mem.o \
+ $(B)/baseq3/game/g_misc.o \
+ $(B)/baseq3/game/g_missile.o \
+ $(B)/baseq3/game/g_mover.o \
+ $(B)/baseq3/game/g_session.o \
+ $(B)/baseq3/game/g_spawn.o \
+ $(B)/baseq3/game/g_svcmds.o \
+ $(B)/baseq3/game/g_syscalls.o \
+ $(B)/baseq3/game/g_target.o \
+ $(B)/baseq3/game/g_team.o \
+ $(B)/baseq3/game/g_trigger.o \
+ $(B)/baseq3/game/g_utils.o \
+ $(B)/baseq3/game/g_weapon.o \
+ \
+ $(B)/baseq3/game/q_math.o \
+ $(B)/baseq3/game/q_shared.o
+
+$(B)/baseq3/qagame$(ARCH).$(SHLIBEXT) : $(Q3GOBJ)
+ $(CC) $(SHLIBLDFLAGS) -o $@ $(Q3GOBJ)
+
+$(B)/baseq3/game/ai_chat.o : $(GDIR)/ai_chat.c; $(DO_SHLIB_CC)
+$(B)/baseq3/game/ai_cmd.o : $(GDIR)/ai_cmd.c; $(DO_SHLIB_CC)
+$(B)/baseq3/game/ai_dmnet.o : $(GDIR)/ai_dmnet.c; $(DO_SHLIB_CC)
+$(B)/baseq3/game/ai_dmq3.o : $(GDIR)/ai_dmq3.c; $(DO_SHLIB_CC)
+$(B)/baseq3/game/ai_main.o : $(GDIR)/ai_main.c; $(DO_SHLIB_CC)
+$(B)/baseq3/game/ai_team.o : $(GDIR)/ai_team.c; $(DO_SHLIB_CC)
+$(B)/baseq3/game/ai_vcmd.o : $(GDIR)/ai_vcmd.c; $(DO_SHLIB_CC)
+$(B)/baseq3/game/bg_misc.o : $(GDIR)/bg_misc.c; $(DO_SHLIB_CC)
+$(B)/baseq3/game/bg_pmove.o : $(GDIR)/bg_pmove.c; $(DO_SHLIB_CC)
+$(B)/baseq3/game/bg_slidemove.o : $(GDIR)/bg_slidemove.c; $(DO_SHLIB_CC)
+$(B)/baseq3/game/g_active.o : $(GDIR)/g_active.c; $(DO_SHLIB_CC)
+$(B)/baseq3/game/g_arenas.o : $(GDIR)/g_arenas.c; $(DO_SHLIB_CC)
+$(B)/baseq3/game/g_bot.o : $(GDIR)/g_bot.c; $(DO_SHLIB_CC)
+$(B)/baseq3/game/g_client.o : $(GDIR)/g_client.c; $(DO_SHLIB_CC)
+$(B)/baseq3/game/g_cmds.o : $(GDIR)/g_cmds.c; $(DO_SHLIB_CC)
+$(B)/baseq3/game/g_combat.o : $(GDIR)/g_combat.c; $(DO_SHLIB_CC)
+$(B)/baseq3/game/g_items.o : $(GDIR)/g_items.c; $(DO_SHLIB_CC)
+$(B)/baseq3/game/g_main.o : $(GDIR)/g_main.c; $(DO_SHLIB_CC)
+$(B)/baseq3/game/g_mem.o : $(GDIR)/g_mem.c; $(DO_SHLIB_CC)
+$(B)/baseq3/game/g_misc.o : $(GDIR)/g_misc.c; $(DO_SHLIB_CC)
+$(B)/baseq3/game/g_missile.o : $(GDIR)/g_missile.c; $(DO_SHLIB_CC)
+$(B)/baseq3/game/g_mover.o : $(GDIR)/g_mover.c; $(DO_SHLIB_CC)
+$(B)/baseq3/game/g_session.o : $(GDIR)/g_session.c; $(DO_SHLIB_CC)
+$(B)/baseq3/game/g_spawn.o : $(GDIR)/g_spawn.c; $(DO_SHLIB_CC)
+$(B)/baseq3/game/g_svcmds.o : $(GDIR)/g_svcmds.c; $(DO_SHLIB_CC)
+$(B)/baseq3/game/g_syscalls.o : $(GDIR)/g_syscalls.c; $(DO_SHLIB_CC)
+$(B)/baseq3/game/g_target.o : $(GDIR)/g_target.c; $(DO_SHLIB_CC)
+$(B)/baseq3/game/g_team.o : $(GDIR)/g_team.c; $(DO_SHLIB_CC)
+$(B)/baseq3/game/g_trigger.o : $(GDIR)/g_trigger.c; $(DO_SHLIB_CC)
+$(B)/baseq3/game/g_utils.o : $(GDIR)/g_utils.c; $(DO_SHLIB_CC)
+$(B)/baseq3/game/g_weapon.o : $(GDIR)/g_weapon.c; $(DO_SHLIB_CC)
+$(B)/baseq3/game/q_math.o : $(GDIR)/q_math.c; $(DO_SHLIB_CC)
+$(B)/baseq3/game/q_shared.o : $(GDIR)/q_shared.c; $(DO_SHLIB_CC)
+
+
+#############################################################################
+## MISSIONPACK GAME
+#############################################################################
+
+MPGOBJ = \
+ $(B)/missionpack/game/ai_chat.o \
+ $(B)/missionpack/game/ai_cmd.o \
+ $(B)/missionpack/game/ai_dmnet.o \
+ $(B)/missionpack/game/ai_dmq3.o \
+ $(B)/missionpack/game/ai_main.o \
+ $(B)/missionpack/game/ai_team.o \
+ $(B)/missionpack/game/ai_vcmd.o \
+ $(B)/missionpack/game/bg_misc.o \
+ $(B)/missionpack/game/bg_pmove.o \
+ $(B)/missionpack/game/bg_slidemove.o \
+ $(B)/missionpack/game/g_active.o \
+ $(B)/missionpack/game/g_arenas.o \
+ $(B)/missionpack/game/g_bot.o \
+ $(B)/missionpack/game/g_client.o \
+ $(B)/missionpack/game/g_cmds.o \
+ $(B)/missionpack/game/g_combat.o \
+ $(B)/missionpack/game/g_items.o \
+ $(B)/missionpack/game/g_main.o \
+ $(B)/missionpack/game/g_mem.o \
+ $(B)/missionpack/game/g_misc.o \
+ $(B)/missionpack/game/g_missile.o \
+ $(B)/missionpack/game/g_mover.o \
+ $(B)/missionpack/game/g_session.o \
+ $(B)/missionpack/game/g_spawn.o \
+ $(B)/missionpack/game/g_svcmds.o \
+ $(B)/missionpack/game/g_syscalls.o \
+ $(B)/missionpack/game/g_target.o \
+ $(B)/missionpack/game/g_team.o \
+ $(B)/missionpack/game/g_trigger.o \
+ $(B)/missionpack/game/g_utils.o \
+ $(B)/missionpack/game/g_weapon.o \
+ \
+ $(B)/missionpack/game/q_math.o \
+ $(B)/missionpack/game/q_shared.o
+
+$(B)/missionpack/qagame$(ARCH).$(SHLIBEXT) : $(MPGOBJ)
+ $(CC) $(SHLIBLDFLAGS) -o $@ $(MPGOBJ)
+
+$(B)/missionpack/game/ai_chat.o : $(GDIR)/ai_chat.c; $(DO_SHLIB_CC) -DMISSIONPACK
+$(B)/missionpack/game/ai_cmd.o : $(GDIR)/ai_cmd.c; $(DO_SHLIB_CC) -DMISSIONPACK
+$(B)/missionpack/game/ai_dmnet.o : $(GDIR)/ai_dmnet.c; $(DO_SHLIB_CC) -DMISSIONPACK
+$(B)/missionpack/game/ai_dmq3.o : $(GDIR)/ai_dmq3.c; $(DO_SHLIB_CC) -DMISSIONPACK
+$(B)/missionpack/game/ai_main.o : $(GDIR)/ai_main.c; $(DO_SHLIB_CC) -DMISSIONPACK
+$(B)/missionpack/game/ai_team.o : $(GDIR)/ai_team.c; $(DO_SHLIB_CC) -DMISSIONPACK
+$(B)/missionpack/game/ai_vcmd.o : $(GDIR)/ai_vcmd.c; $(DO_SHLIB_CC) -DMISSIONPACK
+$(B)/missionpack/game/bg_misc.o : $(GDIR)/bg_misc.c; $(DO_SHLIB_CC) -DMISSIONPACK
+$(B)/missionpack/game/bg_pmove.o : $(GDIR)/bg_pmove.c; $(DO_SHLIB_CC) -DMISSIONPACK
+$(B)/missionpack/game/bg_slidemove.o : $(GDIR)/bg_slidemove.c; $(DO_SHLIB_CC) -DMISSIONPACK
+$(B)/missionpack/game/g_active.o : $(GDIR)/g_active.c; $(DO_SHLIB_CC) -DMISSIONPACK
+$(B)/missionpack/game/g_arenas.o : $(GDIR)/g_arenas.c; $(DO_SHLIB_CC) -DMISSIONPACK
+$(B)/missionpack/game/g_bot.o : $(GDIR)/g_bot.c; $(DO_SHLIB_CC) -DMISSIONPACK
+$(B)/missionpack/game/g_client.o : $(GDIR)/g_client.c; $(DO_SHLIB_CC) -DMISSIONPACK
+$(B)/missionpack/game/g_cmds.o : $(GDIR)/g_cmds.c; $(DO_SHLIB_CC) -DMISSIONPACK
+$(B)/missionpack/game/g_combat.o : $(GDIR)/g_combat.c; $(DO_SHLIB_CC) -DMISSIONPACK
+$(B)/missionpack/game/g_items.o : $(GDIR)/g_items.c; $(DO_SHLIB_CC) -DMISSIONPACK
+$(B)/missionpack/game/g_main.o : $(GDIR)/g_main.c; $(DO_SHLIB_CC) -DMISSIONPACK
+$(B)/missionpack/game/g_mem.o : $(GDIR)/g_mem.c; $(DO_SHLIB_CC) -DMISSIONPACK
+$(B)/missionpack/game/g_misc.o : $(GDIR)/g_misc.c; $(DO_SHLIB_CC) -DMISSIONPACK
+$(B)/missionpack/game/g_missile.o : $(GDIR)/g_missile.c; $(DO_SHLIB_CC) -DMISSIONPACK
+$(B)/missionpack/game/g_mover.o : $(GDIR)/g_mover.c; $(DO_SHLIB_CC) -DMISSIONPACK
+$(B)/missionpack/game/g_session.o : $(GDIR)/g_session.c; $(DO_SHLIB_CC) -DMISSIONPACK
+$(B)/missionpack/game/g_spawn.o : $(GDIR)/g_spawn.c; $(DO_SHLIB_CC) -DMISSIONPACK
+$(B)/missionpack/game/g_svcmds.o : $(GDIR)/g_svcmds.c; $(DO_SHLIB_CC) -DMISSIONPACK
+$(B)/missionpack/game/g_syscalls.o : $(GDIR)/g_syscalls.c; $(DO_SHLIB_CC) -DMISSIONPACK
+$(B)/missionpack/game/g_target.o : $(GDIR)/g_target.c; $(DO_SHLIB_CC) -DMISSIONPACK
+$(B)/missionpack/game/g_team.o : $(GDIR)/g_team.c; $(DO_SHLIB_CC) -DMISSIONPACK
+$(B)/missionpack/game/g_trigger.o : $(GDIR)/g_trigger.c; $(DO_SHLIB_CC) -DMISSIONPACK
+$(B)/missionpack/game/g_utils.o : $(GDIR)/g_utils.c; $(DO_SHLIB_CC) -DMISSIONPACK
+$(B)/missionpack/game/g_weapon.o : $(GDIR)/g_weapon.c; $(DO_SHLIB_CC) -DMISSIONPACK
+$(B)/missionpack/game/q_math.o : $(GDIR)/q_math.c; $(DO_SHLIB_CC) -DMISSIONPACK
+$(B)/missionpack/game/q_shared.o : $(GDIR)/q_shared.c; $(DO_SHLIB_CC) -DMISSIONPACK
+
+
+
+#############################################################################
+## BASEQ3 UI
+#############################################################################
+
+Q3UIOBJ = \
+ $(B)/baseq3/ui/bg_misc.o \
+ $(B)/baseq3/ui/ui_addbots.o \
+ $(B)/baseq3/ui/ui_atoms.o \
+ $(B)/baseq3/ui/ui_cdkey.o \
+ $(B)/baseq3/ui/ui_cinematics.o \
+ $(B)/baseq3/ui/ui_confirm.o \
+ $(B)/baseq3/ui/ui_connect.o \
+ $(B)/baseq3/ui/ui_controls2.o \
+ $(B)/baseq3/ui/ui_credits.o \
+ $(B)/baseq3/ui/ui_demo2.o \
+ $(B)/baseq3/ui/ui_display.o \
+ $(B)/baseq3/ui/ui_gameinfo.o \
+ $(B)/baseq3/ui/ui_ingame.o \
+ $(B)/baseq3/ui/ui_loadconfig.o \
+ $(B)/baseq3/ui/ui_main.o \
+ $(B)/baseq3/ui/ui_menu.o \
+ $(B)/baseq3/ui/ui_mfield.o \
+ $(B)/baseq3/ui/ui_mods.o \
+ $(B)/baseq3/ui/ui_network.o \
+ $(B)/baseq3/ui/ui_options.o \
+ $(B)/baseq3/ui/ui_playermodel.o \
+ $(B)/baseq3/ui/ui_players.o \
+ $(B)/baseq3/ui/ui_playersettings.o \
+ $(B)/baseq3/ui/ui_preferences.o \
+ $(B)/baseq3/ui/ui_qmenu.o \
+ $(B)/baseq3/ui/ui_removebots.o \
+ $(B)/baseq3/ui/ui_saveconfig.o \
+ $(B)/baseq3/ui/ui_serverinfo.o \
+ $(B)/baseq3/ui/ui_servers2.o \
+ $(B)/baseq3/ui/ui_setup.o \
+ $(B)/baseq3/ui/ui_sound.o \
+ $(B)/baseq3/ui/ui_sparena.o \
+ $(B)/baseq3/ui/ui_specifyserver.o \
+ $(B)/baseq3/ui/ui_splevel.o \
+ $(B)/baseq3/ui/ui_sppostgame.o \
+ $(B)/baseq3/ui/ui_spskill.o \
+ $(B)/baseq3/ui/ui_startserver.o \
+ $(B)/baseq3/ui/ui_syscalls.o \
+ $(B)/baseq3/ui/ui_team.o \
+ $(B)/baseq3/ui/ui_teamorders.o \
+ $(B)/baseq3/ui/ui_video.o \
+ \
+ $(B)/baseq3/ui/q_math.o \
+ $(B)/baseq3/ui/q_shared.o
+
+$(B)/baseq3/ui$(ARCH).$(SHLIBEXT) : $(Q3UIOBJ)
+ $(CC) $(CFLAGS) $(SHLIBLDFLAGS) -o $@ $(Q3UIOBJ)
+
+$(B)/baseq3/ui/bg_misc.o : $(GDIR)/bg_misc.c; $(DO_SHLIB_CC)
+$(B)/baseq3/ui/ui_addbots.o : $(Q3UIDIR)/ui_addbots.c; $(DO_SHLIB_CC)
+$(B)/baseq3/ui/ui_atoms.o : $(Q3UIDIR)/ui_atoms.c; $(DO_SHLIB_CC)
+$(B)/baseq3/ui/ui_cinematics.o : $(Q3UIDIR)/ui_cinematics.c; $(DO_SHLIB_CC)
+$(B)/baseq3/ui/ui_cdkey.o : $(Q3UIDIR)/ui_cdkey.c; $(DO_SHLIB_CC)
+$(B)/baseq3/ui/ui_confirm.o : $(Q3UIDIR)/ui_confirm.c; $(DO_SHLIB_CC)
+$(B)/baseq3/ui/ui_connect.o : $(Q3UIDIR)/ui_connect.c; $(DO_SHLIB_CC)
+$(B)/baseq3/ui/ui_controls2.o : $(Q3UIDIR)/ui_controls2.c; $(DO_SHLIB_CC)
+$(B)/baseq3/ui/ui_credits.o : $(Q3UIDIR)/ui_credits.c; $(DO_SHLIB_CC)
+$(B)/baseq3/ui/ui_demo2.o : $(Q3UIDIR)/ui_demo2.c; $(DO_SHLIB_CC)
+$(B)/baseq3/ui/ui_display.o : $(Q3UIDIR)/ui_display.c; $(DO_SHLIB_CC)
+$(B)/baseq3/ui/ui_gameinfo.o : $(Q3UIDIR)/ui_gameinfo.c; $(DO_SHLIB_CC)
+$(B)/baseq3/ui/ui_ingame.o : $(Q3UIDIR)/ui_ingame.c; $(DO_SHLIB_CC)
+$(B)/baseq3/ui/ui_loadconfig.o : $(Q3UIDIR)/ui_loadconfig.c; $(DO_SHLIB_CC)
+$(B)/baseq3/ui/ui_main.o : $(Q3UIDIR)/ui_main.c; $(DO_SHLIB_CC)
+$(B)/baseq3/ui/ui_menu.o : $(Q3UIDIR)/ui_menu.c; $(DO_SHLIB_CC)
+$(B)/baseq3/ui/ui_mfield.o : $(Q3UIDIR)/ui_mfield.c; $(DO_SHLIB_CC)
+$(B)/baseq3/ui/ui_mods.o : $(Q3UIDIR)/ui_mods.c; $(DO_SHLIB_CC)
+$(B)/baseq3/ui/ui_network.o : $(Q3UIDIR)/ui_network.c; $(DO_SHLIB_CC)
+$(B)/baseq3/ui/ui_options.o : $(Q3UIDIR)/ui_options.c; $(DO_SHLIB_CC)
+$(B)/baseq3/ui/ui_playermodel.o : $(Q3UIDIR)/ui_playermodel.c; $(DO_SHLIB_CC)
+$(B)/baseq3/ui/ui_players.o : $(Q3UIDIR)/ui_players.c; $(DO_SHLIB_CC)
+$(B)/baseq3/ui/ui_playersettings.o : $(Q3UIDIR)/ui_playersettings.c; $(DO_SHLIB_CC)
+$(B)/baseq3/ui/ui_preferences.o : $(Q3UIDIR)/ui_preferences.c; $(DO_SHLIB_CC)
+$(B)/baseq3/ui/ui_qmenu.o : $(Q3UIDIR)/ui_qmenu.c; $(DO_SHLIB_CC)
+$(B)/baseq3/ui/ui_quit.o : $(Q3UIDIR)/ui_quit.c; $(DO_SHLIB_CC)
+$(B)/baseq3/ui/ui_removebots.o : $(Q3UIDIR)/ui_removebots.c; $(DO_SHLIB_CC)
+$(B)/baseq3/ui/ui_saveconfig.o : $(Q3UIDIR)/ui_saveconfig.c; $(DO_SHLIB_CC)
+$(B)/baseq3/ui/ui_serverinfo.o : $(Q3UIDIR)/ui_serverinfo.c; $(DO_SHLIB_CC)
+$(B)/baseq3/ui/ui_servers2.o : $(Q3UIDIR)/ui_servers2.c; $(DO_SHLIB_CC)
+$(B)/baseq3/ui/ui_setup.o : $(Q3UIDIR)/ui_setup.c; $(DO_SHLIB_CC)
+$(B)/baseq3/ui/ui_sound.o : $(Q3UIDIR)/ui_sound.c; $(DO_SHLIB_CC)
+$(B)/baseq3/ui/ui_sparena.o : $(Q3UIDIR)/ui_sparena.c; $(DO_SHLIB_CC)
+$(B)/baseq3/ui/ui_specifyserver.o : $(Q3UIDIR)/ui_specifyserver.c; $(DO_SHLIB_CC)
+$(B)/baseq3/ui/ui_splevel.o : $(Q3UIDIR)/ui_splevel.c; $(DO_SHLIB_CC)
+$(B)/baseq3/ui/ui_sppostgame.o : $(Q3UIDIR)/ui_sppostgame.c; $(DO_SHLIB_CC)
+$(B)/baseq3/ui/ui_spskill.o : $(Q3UIDIR)/ui_spskill.c; $(DO_SHLIB_CC)
+$(B)/baseq3/ui/ui_startserver.o : $(Q3UIDIR)/ui_startserver.c; $(DO_SHLIB_CC)
+$(B)/baseq3/ui/ui_team.o : $(Q3UIDIR)/ui_team.c; $(DO_SHLIB_CC)
+$(B)/baseq3/ui/ui_teamorders.o : $(Q3UIDIR)/ui_teamorders.c; $(DO_SHLIB_CC)
+$(B)/baseq3/ui/ui_syscalls.o : $(Q3UIDIR)/ui_syscalls.c; $(DO_SHLIB_CC)
+$(B)/baseq3/ui/ui_video.o : $(Q3UIDIR)/ui_video.c; $(DO_SHLIB_CC)
+
+# bk001205 - these wre the only SHLIB compiles in 1.17
+$(B)/baseq3/ui/q_math.o : $(GDIR)/q_math.c; $(DO_SHLIB_CC)
+$(B)/baseq3/ui/q_shared.o : $(GDIR)/q_shared.c; $(DO_SHLIB_CC)
+
+
+
+#############################################################################
+## MISSIONPACK UI
+#############################################################################
+
+MPUIOBJ = \
+ $(B)/missionpack/ui/ui_atoms.o \
+ $(B)/missionpack/ui/ui_gameinfo.o \
+ $(B)/missionpack/ui/ui_main.o \
+ $(B)/missionpack/ui/ui_players.o \
+ $(B)/missionpack/ui/ui_syscalls.o \
+ $(B)/missionpack/ui/ui_util.o \
+ $(B)/missionpack/ui/ui_shared.o \
+ \
+ $(B)/missionpack/ui/bg_misc.o \
+ \
+ $(B)/missionpack/ui/q_math.o \
+ $(B)/missionpack/ui/q_shared.o
+# $(B)/missionpack/ui/ui_atoms.o \
+# $(B)/missionpack/ui/ui_gameinfo.o \
+# $(B)/missionpack/ui/ui_main.o \
+# $(B)/missionpack/ui/ui_players.o \
+# $(B)/missionpack/ui/ui_syscalls.o \
+# $(B)/missionpack/ui/ui_util.o \
+# $(B)/missionpack/ui/ui_shared.o \
+# \
+# $(B)/missionpack/ui/bg_misc.o \
+# $(B)/missionpack/ui/bg_lib.o \
+# \
+# $(B)/missionpack/ui/q_math.o \
+# $(B)/missionpack/ui/q_shared.o
+
+$(B)/missionpack/ui$(ARCH).$(SHLIBEXT) : $(MPUIOBJ)
+ $(CC) $(CFLAGS) $(SHLIBLDFLAGS) -o $@ $(MPUIOBJ)
+
+$(B)/missionpack/ui/ui_atoms.o : $(UIDIR)/ui_atoms.c; $(DO_SHLIB_CC) -DMISSIONPACK
+$(B)/missionpack/ui/ui_gameinfo.o : $(UIDIR)/ui_gameinfo.c; $(DO_SHLIB_CC) -DMISSIONPACK
+$(B)/missionpack/ui/ui_main.o : $(UIDIR)/ui_main.c; $(DO_SHLIB_CC) -DMISSIONPACK
+$(B)/missionpack/ui/ui_players.o : $(UIDIR)/ui_players.c; $(DO_SHLIB_CC) -DMISSIONPACK
+$(B)/missionpack/ui/ui_syscalls.o : $(UIDIR)/ui_syscalls.c; $(DO_SHLIB_CC) -DMISSIONPACK
+$(B)/missionpack/ui/ui_util.o : $(UIDIR)/ui_util.c; $(DO_SHLIB_CC) -DMISSIONPACK
+$(B)/missionpack/ui/ui_shared.o : $(UIDIR)/ui_shared.c; $(DO_SHLIB_CC) -DMISSIONPACK
+
+$(B)/missionpack/ui/bg_misc.o : $(GDIR)/bg_misc.c; $(DO_SHLIB_CC) -DMISSIONPACK
+$(B)/missionpack/ui/bg_lib.o : $(GDIR)/bg_lib.c; $(DO_SHLIB_CC) -DMISSIONPACK
+
+$(B)/missionpack/ui/q_math.o : $(GDIR)/q_math.c; $(DO_SHLIB_CC) -DMISSIONPACK
+$(B)/missionpack/ui/q_shared.o : $(GDIR)/q_shared.c; $(DO_SHLIB_CC) -DMISSIONPACK
+
+
+
+
+
+#############################################################################
+## Q3 STATIC (DEBUG) BUILD
+#############################################################################
+
+Q3SOBJ = \
+ $(B)/q3static/cl_cgame.o \
+ $(B)/q3static/cl_cin.o \
+ $(B)/q3static/cl_console.o \
+ $(B)/q3static/cl_input.o \
+ $(B)/q3static/cl_keys.o \
+ $(B)/q3static/cl_main.o \
+ $(B)/q3static/cl_net_chan.o \
+ $(B)/q3static/cl_parse.o \
+ $(B)/q3static/cl_scrn.o \
+ $(B)/q3static/cl_ui.o \
+ \
+ $(B)/q3static/cm_load.o \
+ $(B)/q3static/cm_patch.o \
+ $(B)/q3static/cm_polylib.o \
+ $(B)/q3static/cm_test.o \
+ $(B)/q3static/cm_trace.o \
+ \
+ $(B)/q3static/cmd.o \
+ $(B)/q3static/common.o \
+ $(B)/q3static/cvar.o \
+ $(B)/q3static/files.o \
+ $(B)/q3static/md4.o \
+ $(B)/q3static/msg.o \
+ $(B)/q3static/net_chan.o \
+ \
+ $(B)/q3static/snd_adpcm.o \
+ $(B)/q3static/snd_dma.o \
+ $(B)/q3static/snd_mem.o \
+ $(B)/q3static/snd_mix.o \
+ $(B)/q3static/snd_wavelet.o \
+ \
+ $(B)/q3static/sv_bot.o \
+ $(B)/q3static/sv_ccmds.o \
+ $(B)/q3static/sv_client.o \
+ $(B)/q3static/sv_game.o \
+ $(B)/q3static/sv_init.o \
+ $(B)/q3static/sv_main.o \
+ $(B)/q3static/sv_net_chan.o \
+ $(B)/q3static/sv_snapshot.o \
+ $(B)/q3static/sv_world.o \
+ \
+ $(B)/q3static/unzip.o \
+ $(B)/q3static/vm.o \
+ $(B)/q3static/vm_interpreted.o \
+ \
+ $(B)/q3static/be_aas_bspq3.o \
+ $(B)/q3static/be_aas_cluster.o \
+ $(B)/q3static/be_aas_debug.o \
+ $(B)/q3static/be_aas_entity.o \
+ $(B)/q3static/be_aas_file.o \
+ $(B)/q3static/be_aas_main.o \
+ $(B)/q3static/be_aas_move.o \
+ $(B)/q3static/be_aas_optimize.o \
+ $(B)/q3static/be_aas_reach.o \
+ $(B)/q3static/be_aas_route.o \
+ $(B)/q3static/be_aas_routealt.o \
+ $(B)/q3static/be_aas_sample.o \
+ $(B)/q3static/be_ai_char.o \
+ $(B)/q3static/be_ai_chat.o \
+ $(B)/q3static/be_ai_gen.o \
+ $(B)/q3static/be_ai_goal.o \
+ $(B)/q3static/be_ai_move.o \
+ $(B)/q3static/be_ai_weap.o \
+ $(B)/q3static/be_ai_weight.o \
+ $(B)/q3static/be_ea.o \
+ $(B)/q3static/be_interface.o \
+ $(B)/q3static/l_crc.o \
+ $(B)/q3static/l_libvar.o \
+ $(B)/q3static/l_log.o \
+ $(B)/q3static/l_memory.o \
+ $(B)/q3static/l_precomp.o \
+ $(B)/q3static/l_script.o \
+ $(B)/q3static/l_struct.o \
+ \
+ $(B)/q3static/jcapimin.o \
+ $(B)/q3static/jchuff.o \
+ $(B)/q3static/jcinit.o \
+ $(B)/q3static/jccoefct.o \
+ $(B)/q3static/jccolor.o \
+ $(B)/q3static/jfdctflt.o \
+ $(B)/q3static/jcdctmgr.o \
+ $(B)/q3static/jcphuff.o \
+ $(B)/q3static/jcmainct.o \
+ $(B)/q3static/jcmarker.o \
+ $(B)/q3static/jcmaster.o \
+ $(B)/q3static/jcomapi.o \
+ $(B)/q3static/jcparam.o \
+ $(B)/q3static/jcprepct.o \
+ $(B)/q3static/jcsample.o \
+ $(B)/q3static/jdapimin.o \
+ $(B)/q3static/jdapistd.o \
+ $(B)/q3static/jdatasrc.o \
+ $(B)/q3static/jdcoefct.o \
+ $(B)/q3static/jdcolor.o \
+ $(B)/q3static/jddctmgr.o \
+ $(B)/q3static/jdhuff.o \
+ $(B)/q3static/jdinput.o \
+ $(B)/q3static/jdmainct.o \
+ $(B)/q3static/jdmarker.o \
+ $(B)/q3static/jdmaster.o \
+ $(B)/q3static/jdpostct.o \
+ $(B)/q3static/jdsample.o \
+ $(B)/q3static/jdtrans.o \
+ $(B)/q3static/jerror.o \
+ $(B)/q3static/jidctflt.o \
+ $(B)/q3static/jmemmgr.o \
+ $(B)/q3static/jmemnobs.o \
+ $(B)/q3static/jutils.o \
+ \
+ $(B)/q3static/tr_animation.o \
+ $(B)/q3static/tr_backend.o \
+ $(B)/q3static/tr_bsp.o \
+ $(B)/q3static/tr_cmds.o \
+ $(B)/q3static/tr_curve.o \
+ $(B)/q3static/tr_flares.o \
+ $(B)/q3static/tr_font.o \
+ $(B)/q3static/tr_image.o \
+ $(B)/q3static/tr_init.o \
+ $(B)/q3static/tr_light.o \
+ $(B)/q3static/tr_main.o \
+ $(B)/q3static/tr_marks.o \
+ $(B)/q3static/tr_mesh.o \
+ $(B)/q3static/tr_model.o \
+ $(B)/q3static/tr_noise.o \
+ $(B)/q3static/tr_scene.o \
+ $(B)/q3static/tr_shade.o \
+ $(B)/q3static/tr_shade_calc.o \
+ $(B)/q3static/tr_shader.o \
+ $(B)/q3static/tr_shadows.o \
+ $(B)/q3static/tr_sky.o \
+ $(B)/q3static/tr_surface.o \
+ $(B)/q3static/tr_world.o \
+ \
+ $(B)/q3static/unix_main.o \
+ $(B)/q3static/unix_net.o \
+ $(B)/q3static/unix_shared.o \
+ \
+ $(B)/q3static/ahoptim.o \
+ $(B)/q3static/autohint.o \
+ $(B)/q3static/ftbase.o \
+ $(B)/q3static/ftdebug.o \
+ $(B)/q3static/ftglyph.o \
+ $(B)/q3static/ftinit.o \
+ $(B)/q3static/ftmm.o \
+ $(B)/q3static/ftsystem.o \
+ $(B)/q3static/raster1.o \
+ $(B)/q3static/sfnt.o \
+ $(B)/q3static/sfobjs.o \
+ $(B)/q3static/smooth.o \
+ $(B)/q3static/truetype.o \
+ \
+ $(B)/q3static/linux_qgl.o \
+ $(B)/q3static/linux_glimp.o \
+ $(B)/q3static/linux_joystick.o \
+ $(B)/q3static/linux_snd.o \
+ $(B)/q3static/snd_mixa.o \
+ $(B)/q3static/matha.o
+
+ifeq ($(ARCH),i386)
+ Q3SOBJ += $(B)/q3static/vm_x86.o
+endif
+
+ifeq ($(ARCH),ppc)
+ ifeq ($(DLL_ONLY),false)
+ Q3SOBJ += $(B)/q3static/vm_ppc.o
+ endif
+endif
+
+
+$(B)/q3static/cl_cgame.o : $(CDIR)/cl_cgame.c; $(DO_CC) -DQ3_STATIC
+$(B)/q3static/cl_cin.o : $(CDIR)/cl_cin.c; $(DO_CC) -DQ3_STATIC
+$(B)/q3static/cl_console.o : $(CDIR)/cl_console.c; $(DO_CC) -DQ3_STATIC
+$(B)/q3static/cl_input.o : $(CDIR)/cl_input.c; $(DO_CC) -DQ3_STATIC
+$(B)/q3static/cl_keys.o : $(CDIR)/cl_keys.c; $(DO_CC) -DQ3_STATIC
+$(B)/q3static/cl_main.o : $(CDIR)/cl_main.c; $(DO_CC) -DQ3_STATIC
+$(B)/q3static/cl_net_chan.o : $(CDIR)/cl_net_chan.c; $(DO_CC) -DQ3_STATIC
+$(B)/q3static/cl_parse.o : $(CDIR)/cl_parse.c; $(DO_CC) -DQ3_STATIC
+$(B)/q3static/cl_scrn.o : $(CDIR)/cl_scrn.c; $(DO_CC) -DQ3_STATIC
+$(B)/q3static/cl_ui.o : $(CDIR)/cl_ui.c; $(DO_CC) -DQ3_STATIC
+$(B)/q3static/snd_adpcm.o : $(CDIR)/snd_adpcm.c; $(DO_CC) -DQ3_STATIC
+$(B)/q3static/snd_dma.o : $(CDIR)/snd_dma.c; $(DO_CC) -DQ3_STATIC
+$(B)/q3static/snd_mem.o : $(CDIR)/snd_mem.c; $(DO_CC) -DQ3_STATIC
+$(B)/q3static/snd_mix.o : $(CDIR)/snd_mix.c; $(DO_CC) -DQ3_STATIC
+$(B)/q3static/snd_wavelet.o : $(CDIR)/snd_wavelet.c; $(DO_CC) -DQ3_STATIC
+$(B)/q3static/sv_bot.o : $(SDIR)/sv_bot.c; $(DO_CC) -DQ3_STATIC
+$(B)/q3static/sv_client.o : $(SDIR)/sv_client.c; $(DO_CC) -DQ3_STATIC
+$(B)/q3static/sv_ccmds.o : $(SDIR)/sv_ccmds.c; $(DO_CC) -DQ3_STATIC
+$(B)/q3static/sv_game.o : $(SDIR)/sv_game.c; $(DO_CC) -DQ3_STATIC
+$(B)/q3static/sv_init.o : $(SDIR)/sv_init.c; $(DO_CC) -DQ3_STATIC
+$(B)/q3static/sv_main.o : $(SDIR)/sv_main.c; $(DO_CC) -DQ3_STATIC
+$(B)/q3static/sv_net_chan.o : $(SDIR)/sv_net_chan.c; $(DO_CC) -DQ3_STATIC
+$(B)/q3static/sv_snapshot.o : $(SDIR)/sv_snapshot.c; $(DO_CC) -DQ3_STATIC
+$(B)/q3static/sv_world.o : $(SDIR)/sv_world.c; $(DO_CC) -DQ3_STATIC
+$(B)/q3static/cm_trace.o : $(CMDIR)/cm_trace.c; $(DO_CC) -DQ3_STATIC
+$(B)/q3static/cm_load.o : $(CMDIR)/cm_load.c; $(DO_CC) -DQ3_STATIC
+$(B)/q3static/cm_test.o : $(CMDIR)/cm_test.c; $(DO_CC) -DQ3_STATIC
+$(B)/q3static/cm_patch.o : $(CMDIR)/cm_patch.c; $(DO_CC) -DQ3_STATIC
+$(B)/q3static/cm_polylib.o : $(CMDIR)/cm_polylib.c; $(DO_CC) -DQ3_STATIC
+$(B)/q3static/cmd.o : $(CMDIR)/cmd.c; $(DO_CC) -DQ3_STATIC
+$(B)/q3static/common.o : $(CMDIR)/common.c; $(DO_CC) -DQ3_STATIC
+$(B)/q3static/cvar.o : $(CMDIR)/cvar.c; $(DO_CC) -DQ3_STATIC
+$(B)/q3static/files.o : $(CMDIR)/files.c; $(DO_CC) -DQ3_STATIC
+$(B)/q3static/md4.o : $(CMDIR)/md4.c; $(DO_CC) -DQ3_STATIC
+$(B)/q3static/msg.o : $(CMDIR)/msg.c; $(DO_CC) -DQ3_STATIC
+$(B)/q3static/net_chan.o : $(CMDIR)/net_chan.c; $(DO_CC) -DQ3_STATIC
+
+$(B)/q3static/be_aas_bspq3.o : $(BLIBDIR)/be_aas_bspq3.c; $(DO_BOT_CC) -DQ3_STATIC
+$(B)/q3static/be_aas_cluster.o : $(BLIBDIR)/be_aas_cluster.c; $(DO_BOT_CC) -DQ3_STATIC
+$(B)/q3static/be_aas_debug.o : $(BLIBDIR)/be_aas_debug.c; $(DO_BOT_CC) -DQ3_STATIC
+$(B)/q3static/be_aas_entity.o : $(BLIBDIR)/be_aas_entity.c; $(DO_BOT_CC) -DQ3_STATIC
+$(B)/q3static/be_aas_file.o : $(BLIBDIR)/be_aas_file.c; $(DO_BOT_CC) -DQ3_STATIC
+$(B)/q3static/be_aas_main.o : $(BLIBDIR)/be_aas_main.c; $(DO_BOT_CC) -DQ3_STATIC
+$(B)/q3static/be_aas_move.o : $(BLIBDIR)/be_aas_move.c; $(DO_BOT_CC) -DQ3_STATIC
+$(B)/q3static/be_aas_optimize.o : $(BLIBDIR)/be_aas_optimize.c; $(DO_BOT_CC) -DQ3_STATIC
+$(B)/q3static/be_aas_reach.o : $(BLIBDIR)/be_aas_reach.c; $(DO_BOT_CC) -DQ3_STATIC
+$(B)/q3static/be_aas_route.o : $(BLIBDIR)/be_aas_route.c; $(DO_BOT_CC) -DQ3_STATIC
+$(B)/q3static/be_aas_routealt.o : $(BLIBDIR)/be_aas_routealt.c; $(DO_BOT_CC) -DQ3_STATIC
+$(B)/q3static/be_aas_sample.o : $(BLIBDIR)/be_aas_sample.c; $(DO_BOT_CC) -DQ3_STATIC
+$(B)/q3static/be_ai_char.o : $(BLIBDIR)/be_ai_char.c; $(DO_BOT_CC) -DQ3_STATIC
+$(B)/q3static/be_ai_chat.o : $(BLIBDIR)/be_ai_chat.c; $(DO_BOT_CC) -DQ3_STATIC
+$(B)/q3static/be_ai_gen.o : $(BLIBDIR)/be_ai_gen.c; $(DO_BOT_CC) -DQ3_STATIC
+$(B)/q3static/be_ai_goal.o : $(BLIBDIR)/be_ai_goal.c; $(DO_BOT_CC) -DQ3_STATIC
+$(B)/q3static/be_ai_move.o : $(BLIBDIR)/be_ai_move.c; $(DO_BOT_CC) -DQ3_STATIC
+$(B)/q3static/be_ai_weap.o : $(BLIBDIR)/be_ai_weap.c; $(DO_BOT_CC) -DQ3_STATIC
+$(B)/q3static/be_ai_weight.o : $(BLIBDIR)/be_ai_weight.c; $(DO_BOT_CC) -DQ3_STATIC
+$(B)/q3static/be_ea.o : $(BLIBDIR)/be_ea.c; $(DO_BOT_CC) -DQ3_STATIC
+$(B)/q3static/be_interface.o : $(BLIBDIR)/be_interface.c; $(DO_BOT_CC) -DQ3_STATIC
+$(B)/q3static/l_crc.o : $(BLIBDIR)/l_crc.c; $(DO_BOT_CC) -DQ3_STATIC
+$(B)/q3static/l_libvar.o : $(BLIBDIR)/l_libvar.c; $(DO_BOT_CC) -DQ3_STATIC
+$(B)/q3static/l_log.o : $(BLIBDIR)/l_log.c; $(DO_BOT_CC) -DQ3_STATIC
+$(B)/q3static/l_memory.o : $(BLIBDIR)/l_memory.c; $(DO_BOT_CC) -DQ3_STATIC
+$(B)/q3static/l_precomp.o : $(BLIBDIR)/l_precomp.c; $(DO_BOT_CC) -DQ3_STATIC
+$(B)/q3static/l_script.o : $(BLIBDIR)/l_script.c; $(DO_BOT_CC) -DQ3_STATIC
+$(B)/q3static/l_struct.o : $(BLIBDIR)/l_struct.c; $(DO_BOT_CC) -DQ3_STATIC
+
+$(B)/q3static/jcapimin.o : $(JPDIR)/jcapimin.c; $(DO_CC) -DQ3_STATIC
+$(B)/q3static/jchuff.o : $(JPDIR)/jchuff.c; $(DO_CC) -DQ3_STATIC
+$(B)/q3static/jcinit.o : $(JPDIR)/jcinit.c; $(DO_CC) -DQ3_STATIC
+$(B)/q3static/jccoefct.o : $(JPDIR)/jccoefct.c; $(DO_CC) -DQ3_STATIC
+$(B)/q3static/jccolor.o : $(JPDIR)/jccolor.c; $(DO_CC) -DQ3_STATIC
+$(B)/q3static/jfdctflt.o : $(JPDIR)/jfdctflt.c; $(DO_CC) -DQ3_STATIC
+$(B)/q3static/jcdctmgr.o : $(JPDIR)/jcdctmgr.c; $(DO_CC) -DQ3_STATIC
+$(B)/q3static/jcmainct.o : $(JPDIR)/jcmainct.c; $(DO_CC) -DQ3_STATIC
+$(B)/q3static/jcmarker.o : $(JPDIR)/jcmarker.c; $(DO_CC) -DQ3_STATIC
+$(B)/q3static/jcmaster.o : $(JPDIR)/jcmaster.c; $(DO_CC) -DQ3_STATIC
+$(B)/q3static/jcomapi.o : $(JPDIR)/jcomapi.c; $(DO_CC) -DQ3_STATIC
+$(B)/q3static/jcparam.o : $(JPDIR)/jcparam.c; $(DO_CC) -DQ3_STATIC
+$(B)/q3static/jcprepct.o : $(JPDIR)/jcprepct.c; $(DO_CC) -DQ3_STATIC
+$(B)/q3static/jcsample.o : $(JPDIR)/jcsample.c; $(DO_CC) -DQ3_STATIC
+
+$(B)/q3static/jdapimin.o : $(JPDIR)/jdapimin.c; $(DO_CC) -DQ3_STATIC
+$(B)/q3static/jdapistd.o : $(JPDIR)/jdapistd.c; $(DO_CC) -DQ3_STATIC
+$(B)/q3static/jdatasrc.o : $(JPDIR)/jdatasrc.c; $(DO_CC) -DQ3_STATIC
+$(B)/q3static/jdcoefct.o : $(JPDIR)/jdcoefct.c; $(DO_CC) -DQ3_STATIC
+$(B)/q3static/jdcolor.o : $(JPDIR)/jdcolor.c; $(DO_CC) -DQ3_STATIC
+$(B)/q3static/jcphuff.o : $(JPDIR)/jcphuff.c; $(DO_CC) -DQ3_STATIC
+$(B)/q3static/jddctmgr.o : $(JPDIR)/jddctmgr.c; $(DO_CC) -DQ3_STATIC
+$(B)/q3static/jdhuff.o : $(JPDIR)/jdhuff.c; $(DO_CC) -DQ3_STATIC
+$(B)/q3static/jdinput.o : $(JPDIR)/jdinput.c; $(DO_CC) -DQ3_STATIC
+$(B)/q3static/jdmainct.o : $(JPDIR)/jdmainct.c; $(DO_CC) -DQ3_STATIC
+$(B)/q3static/jdmarker.o : $(JPDIR)/jdmarker.c; $(DO_CC) -DQ3_STATIC
+$(B)/q3static/jdmaster.o : $(JPDIR)/jdmaster.c; $(DO_CC) -DQ3_STATIC
+$(B)/q3static/jdpostct.o : $(JPDIR)/jdpostct.c; $(DO_CC) -DQ3_STATIC
+$(B)/q3static/jdsample.o : $(JPDIR)/jdsample.c; $(DO_CC) -DQ3_STATIC
+$(B)/q3static/jdtrans.o : $(JPDIR)/jdtrans.c; $(DO_CC) -DQ3_STATIC
+$(B)/q3static/jerror.o : $(JPDIR)/jerror.c; $(DO_CC) -DQ3_STATIC
+$(B)/q3static/jidctflt.o : $(JPDIR)/jidctflt.c; $(DO_CC) -DQ3_STATIC
+$(B)/q3static/jmemmgr.o : $(JPDIR)/jmemmgr.c; $(DO_CC) -DQ3_STATIC
+$(B)/q3static/jmemnobs.o : $(JPDIR)/jmemnobs.c; $(DO_CC) -DQ3_STATIC
+$(B)/q3static/jutils.o : $(JPDIR)/jutils.c; $(DO_CC) -DQ3_STATIC
+
+$(B)/q3static/tr_bsp.o : $(RDIR)/tr_bsp.c; $(DO_CC) -DQ3_STATIC
+$(B)/q3static/tr_animation.o : $(RDIR)/tr_animation.c; $(DO_CC) -DQ3_STATIC
+$(B)/q3static/tr_backend.o : $(RDIR)/tr_backend.c; $(DO_CC) -DQ3_STATIC
+$(B)/q3static/tr_cmds.o : $(RDIR)/tr_cmds.c; $(DO_CC) -DQ3_STATIC
+$(B)/q3static/tr_curve.o : $(RDIR)/tr_curve.c; $(DO_CC) -DQ3_STATIC
+$(B)/q3static/tr_flares.o : $(RDIR)/tr_flares.c; $(DO_CC) -DQ3_STATIC
+$(B)/q3static/tr_font.o : $(RDIR)/tr_font.c; $(DO_CC) -DQ3_STATIC
+$(B)/q3static/tr_image.o : $(RDIR)/tr_image.c; $(DO_CC) -DQ3_STATIC
+$(B)/q3static/tr_init.o : $(RDIR)/tr_init.c; $(DO_CC) -DQ3_STATIC
+$(B)/q3static/tr_light.o : $(RDIR)/tr_light.c; $(DO_CC) -DQ3_STATIC
+$(B)/q3static/tr_main.o : $(RDIR)/tr_main.c; $(DO_CC) -DQ3_STATIC
+$(B)/q3static/tr_marks.o : $(RDIR)/tr_marks.c; $(DO_CC) -DQ3_STATIC
+$(B)/q3static/tr_mesh.o : $(RDIR)/tr_mesh.c; $(DO_CC) -DQ3_STATIC
+$(B)/q3static/tr_model.o : $(RDIR)/tr_model.c; $(DO_CC) -DQ3_STATIC
+$(B)/q3static/tr_noise.o : $(RDIR)/tr_noise.c; $(DO_CC) -DQ3_STATIC
+$(B)/q3static/tr_scene.o : $(RDIR)/tr_scene.c; $(DO_CC) -DQ3_STATIC
+$(B)/q3static/tr_shade.o : $(RDIR)/tr_shade.c; $(DO_CC) -DQ3_STATIC
+$(B)/q3static/tr_shader.o : $(RDIR)/tr_shader.c; $(DO_CC) -DQ3_STATIC
+$(B)/q3static/tr_shade_calc.o : $(RDIR)/tr_shade_calc.c; $(DO_CC) -DQ3_STATIC
+$(B)/q3static/tr_shadows.o : $(RDIR)/tr_shadows.c; $(DO_CC) -DQ3_STATIC
+$(B)/q3static/tr_sky.o : $(RDIR)/tr_sky.c; $(DO_CC) -DQ3_STATIC
+$(B)/q3static/tr_smp.o : $(RDIR)/tr_smp.c; $(DO_CC) -DQ3_STATIC
+$(B)/q3static/tr_stripify.o : $(RDIR)/tr_stripify.c; $(DO_CC) -DQ3_STATIC
+$(B)/q3static/tr_subdivide.o : $(RDIR)/tr_subdivide.c; $(DO_CC) -DQ3_STATIC
+$(B)/q3static/tr_surface.o : $(RDIR)/tr_surface.c; $(DO_CC) -DQ3_STATIC
+$(B)/q3static/tr_world.o : $(RDIR)/tr_world.c; $(DO_CC) -DQ3_STATIC
+
+$(B)/q3static/unix_qgl.o : $(UDIR)/unix_qgl.c; $(DO_CC) -DQ3_STATIC
+$(B)/q3static/unix_main.o : $(UDIR)/unix_main.c; $(DO_CC) -DQ3_STATIC
+$(B)/q3static/unix_net.o : $(UDIR)/unix_net.c; $(DO_CC) -DQ3_STATIC
+$(B)/q3static/unix_shared.o : $(UDIR)/unix_shared.c; $(DO_CC) -DQ3_STATIC
+$(B)/q3static/linux_glimp.o : $(UDIR)/linux_glimp.c; $(DO_CC) -DQ3_STATIC
+$(B)/q3static/linux_joystick.o : $(UDIR)/linux_joystick.c; $(DO_CC) -DQ3_STATIC
+$(B)/q3static/linux_qgl.o : $(UDIR)/linux_qgl.c; $(DO_CC) -DQ3_STATIC
+$(B)/q3static/linux_input.o : $(UDIR)/linux_input.c; $(DO_CC) -DQ3_STATIC
+$(B)/q3static/linux_snd.o : $(UDIR)/linux_snd.c; $(DO_CC) -DQ3_STATIC
+$(B)/q3static/snd_mixa.o : $(UDIR)/snd_mixa.s; $(DO_AS)
+$(B)/q3static/matha.o : $(UDIR)/matha.s; $(DO_AS)
+$(B)/q3static/unzip.o : $(CMDIR)/unzip.c; $(DO_CC) -DQ3_STATIC
+$(B)/q3static/vm.o : $(CMDIR)/vm.c; $(DO_CC) -DQ3_STATIC
+$(B)/q3static/vm_interpreted.o : $(CMDIR)/vm_interpreted.c; $(DO_CC) -DQ3_STATIC
+
+ifeq ($(ARCH),i386)
+ $(B)/q3static/vm_x86.o : $(CMDIR)/vm_x86.c; $(DO_CC) -DQ3_STATIC
+endif
+
+ifeq ($(ARCH),ppc)
+ifeq ($(DLL_ONLY),false)
+$(B)/q3static/vm_ppc.o : $(CMDIR)/vm_ppc.c; $(DO_CC) -DQ3_STATIC
+endif
+endif
+
+$(B)/q3static/ahoptim.o : $(FTDIR)/ahoptim.c; $(DO_CC) -DQ3_STATIC
+$(B)/q3static/autohint.o : $(FTDIR)/autohint.c; $(DO_CC) -DQ3_STATIC
+$(B)/q3static/ftbase.o : $(FTDIR)/ftbase.c; $(DO_CC) -DQ3_STATIC
+$(B)/q3static/ftdebug.o : $(FTDIR)/ftdebug.c; $(DO_CC) -DQ3_STATIC
+$(B)/q3static/ftglyph.o : $(FTDIR)/ftglyph.c; $(DO_CC) -DQ3_STATIC
+$(B)/q3static/ftinit.o : $(FTDIR)/ftinit.c; $(DO_CC) -DQ3_STATIC
+$(B)/q3static/ftmm.o : $(FTDIR)/ftmm.c; $(DO_CC) -DQ3_STATIC
+$(B)/q3static/ftsystem.o : $(FTDIR)/ftsystem.c; $(DO_CC) -DQ3_STATIC
+$(B)/q3static/raster1.o : $(FTDIR)/raster1.c; $(DO_CC) -DQ3_STATIC -DFT_FLAT_COMPILE
+$(B)/q3static/sfnt.o : $(FTDIR)/sfnt.c; $(DO_CC) -DQ3_STATIC
+$(B)/q3static/sfobjs.o : $(FTDIR)/sfobjs.c; $(DO_CC) -DQ3_STATIC
+$(B)/q3static/smooth.o : $(FTDIR)/smooth.c; $(DO_CC) -DQ3_STATIC -DFT_FLAT_COMPILE
+$(B)/q3static/truetype.o : $(FTDIR)/truetype.c; $(DO_CC) -DQ3_STATIC
+
+## add BASEQ3 CGAME
+Q3SOBJ += \
+ $(B)/q3static/cg_consolecmds.o \
+ $(B)/q3static/cg_draw.o \
+ $(B)/q3static/cg_drawtools.o \
+ $(B)/q3static/cg_effects.o \
+ $(B)/q3static/cg_ents.o \
+ $(B)/q3static/cg_event.o \
+ $(B)/q3static/cg_info.o \
+ $(B)/q3static/cg_localents.o \
+ $(B)/q3static/cg_main.o \
+ $(B)/q3static/cg_marks.o \
+ $(B)/q3static/cg_players.o \
+ $(B)/q3static/cg_playerstate.o \
+ $(B)/q3static/cg_predict.o \
+ $(B)/q3static/cg_scoreboard.o \
+ $(B)/q3static/cg_servercmds.o \
+ $(B)/q3static/cg_snapshot.o \
+ $(B)/q3static/cg_syscalls.o \
+ $(B)/q3static/cg_view.o \
+ $(B)/q3static/cg_weapons.o
+
+$(B)/q3static/cg_consolecmds.o : $(CGDIR)/cg_consolecmds.c; $(DO_CC) -DQ3_STATIC
+$(B)/q3static/cg_draw.o : $(CGDIR)/cg_draw.c; $(DO_CC) -DQ3_STATIC
+$(B)/q3static/cg_drawtools.o : $(CGDIR)/cg_drawtools.c; $(DO_CC) -DQ3_STATIC
+$(B)/q3static/cg_effects.o : $(CGDIR)/cg_effects.c; $(DO_CC) -DQ3_STATIC
+$(B)/q3static/cg_ents.o : $(CGDIR)/cg_ents.c; $(DO_CC) -DQ3_STATIC
+$(B)/q3static/cg_event.o : $(CGDIR)/cg_event.c; $(DO_CC) -DQ3_STATIC
+$(B)/q3static/cg_info.o : $(CGDIR)/cg_info.c; $(DO_CC) -DQ3_STATIC
+$(B)/q3static/cg_localents.o : $(CGDIR)/cg_localents.c; $(DO_CC) -DQ3_STATIC
+$(B)/q3static/cg_main.o : $(CGDIR)/cg_main.c; $(DO_CC) -DQ3_STATIC
+$(B)/q3static/cg_marks.o : $(CGDIR)/cg_marks.c; $(DO_CC) -DQ3_STATIC
+$(B)/q3static/cg_players.o : $(CGDIR)/cg_players.c; $(DO_CC) -DQ3_STATIC
+$(B)/q3static/cg_playerstate.o : $(CGDIR)/cg_playerstate.c; $(DO_CC) -DQ3_STATIC
+$(B)/q3static/cg_predict.o : $(CGDIR)/cg_predict.c; $(DO_CC) -DQ3_STATIC
+$(B)/q3static/cg_scoreboard.o : $(CGDIR)/cg_scoreboard.c; $(DO_CC) -DQ3_STATIC
+$(B)/q3static/cg_servercmds.o : $(CGDIR)/cg_servercmds.c; $(DO_CC) -DQ3_STATIC
+$(B)/q3static/cg_snapshot.o : $(CGDIR)/cg_snapshot.c; $(DO_CC) -DQ3_STATIC
+$(B)/q3static/cg_syscalls.o : $(CGDIR)/cg_syscalls.c; $(DO_CC) -DQ3_STATIC
+$(B)/q3static/cg_view.o : $(CGDIR)/cg_view.c; $(DO_CC) -DQ3_STATIC
+$(B)/q3static/cg_weapons.o : $(CGDIR)/cg_weapons.c; $(DO_CC) -DQ3_STATIC
+
+## add BASEQ3 GAME
+Q3SOBJ += \
+ $(B)/q3static/ai_chat.o \
+ $(B)/q3static/ai_cmd.o \
+ $(B)/q3static/ai_dmnet.o \
+ $(B)/q3static/ai_dmq3.o \
+ $(B)/q3static/ai_main.o \
+ $(B)/q3static/ai_team.o \
+ $(B)/q3static/ai_vcmd.o \
+ $(B)/q3static/g_active.o \
+ $(B)/q3static/g_arenas.o \
+ $(B)/q3static/g_bot.o \
+ $(B)/q3static/g_client.o \
+ $(B)/q3static/g_cmds.o \
+ $(B)/q3static/g_combat.o \
+ $(B)/q3static/g_items.o \
+ $(B)/q3static/g_main.o \
+ $(B)/q3static/g_mem.o \
+ $(B)/q3static/g_misc.o \
+ $(B)/q3static/g_missile.o \
+ $(B)/q3static/g_mover.o \
+ $(B)/q3static/g_session.o \
+ $(B)/q3static/g_spawn.o \
+ $(B)/q3static/g_svcmds.o \
+ $(B)/q3static/g_target.o \
+ $(B)/q3static/g_team.o \
+ $(B)/q3static/g_trigger.o \
+ $(B)/q3static/g_utils.o \
+ $(B)/q3static/g_weapon.o \
+ \
+ $(B)/q3static/g_syscalls.o
+
+$(B)/q3static/ai_chat.o : $(GDIR)/ai_chat.c; $(DO_CC) -DQ3_STATIC
+$(B)/q3static/ai_cmd.o : $(GDIR)/ai_cmd.c; $(DO_CC) -DQ3_STATIC
+$(B)/q3static/ai_dmnet.o : $(GDIR)/ai_dmnet.c; $(DO_CC) -DQ3_STATIC
+$(B)/q3static/ai_dmq3.o : $(GDIR)/ai_dmq3.c; $(DO_CC) -DQ3_STATIC
+$(B)/q3static/ai_main.o : $(GDIR)/ai_main.c; $(DO_CC) -DQ3_STATIC
+$(B)/q3static/ai_team.o : $(GDIR)/ai_team.c; $(DO_CC) -DQ3_STATIC
+$(B)/q3static/ai_vcmd.o : $(GDIR)/ai_vcmd.c; $(DO_CC) -DQ3_STATIC
+$(B)/q3static/g_active.o : $(GDIR)/g_active.c; $(DO_CC) -DQ3_STATIC
+$(B)/q3static/g_arenas.o : $(GDIR)/g_arenas.c; $(DO_CC) -DQ3_STATIC
+$(B)/q3static/g_bot.o : $(GDIR)/g_bot.c; $(DO_CC) -DQ3_STATIC
+$(B)/q3static/g_client.o : $(GDIR)/g_client.c; $(DO_CC) -DQ3_STATIC
+$(B)/q3static/g_cmds.o : $(GDIR)/g_cmds.c; $(DO_CC) -DQ3_STATIC
+$(B)/q3static/g_combat.o : $(GDIR)/g_combat.c; $(DO_CC) -DQ3_STATIC
+$(B)/q3static/g_items.o : $(GDIR)/g_items.c; $(DO_CC) -DQ3_STATIC
+$(B)/q3static/g_main.o : $(GDIR)/g_main.c; $(DO_CC) -DQ3_STATIC
+$(B)/q3static/g_mem.o : $(GDIR)/g_mem.c; $(DO_CC) -DQ3_STATIC
+$(B)/q3static/g_misc.o : $(GDIR)/g_misc.c; $(DO_CC) -DQ3_STATIC
+$(B)/q3static/g_missile.o : $(GDIR)/g_missile.c; $(DO_CC) -DQ3_STATIC
+$(B)/q3static/g_mover.o : $(GDIR)/g_mover.c; $(DO_CC) -DQ3_STATIC
+$(B)/q3static/g_session.o : $(GDIR)/g_session.c; $(DO_CC) -DQ3_STATIC
+$(B)/q3static/g_spawn.o : $(GDIR)/g_spawn.c; $(DO_CC) -DQ3_STATIC
+$(B)/q3static/g_svcmds.o : $(GDIR)/g_svcmds.c; $(DO_CC) -DQ3_STATIC
+$(B)/q3static/g_syscalls.o : $(GDIR)/g_syscalls.c; $(DO_CC) -DQ3_STATIC
+$(B)/q3static/g_target.o : $(GDIR)/g_target.c; $(DO_CC) -DQ3_STATIC
+$(B)/q3static/g_team.o : $(GDIR)/g_team.c; $(DO_CC) -DQ3_STATIC
+$(B)/q3static/g_trigger.o : $(GDIR)/g_trigger.c; $(DO_CC) -DQ3_STATIC
+$(B)/q3static/g_utils.o : $(GDIR)/g_utils.c; $(DO_CC) -DQ3_STATIC
+$(B)/q3static/g_weapon.o : $(GDIR)/g_weapon.c; $(DO_CC) -DQ3_STATIC
+
+## add BASEQ3 UI
+Q3SOBJ += \
+ $(B)/q3static/ui_addbots.o \
+ $(B)/q3static/ui_atoms.o \
+ $(B)/q3static/ui_cdkey.o \
+ $(B)/q3static/ui_cinematics.o \
+ $(B)/q3static/ui_confirm.o \
+ $(B)/q3static/ui_connect.o \
+ $(B)/q3static/ui_controls2.o \
+ $(B)/q3static/ui_credits.o \
+ $(B)/q3static/ui_demo2.o \
+ $(B)/q3static/ui_display.o \
+ $(B)/q3static/ui_gameinfo.o \
+ $(B)/q3static/ui_ingame.o \
+ $(B)/q3static/ui_loadconfig.o \
+ $(B)/q3static/ui_main.o \
+ $(B)/q3static/ui_menu.o \
+ $(B)/q3static/ui_mfield.o \
+ $(B)/q3static/ui_mods.o \
+ $(B)/q3static/ui_network.o \
+ $(B)/q3static/ui_options.o \
+ $(B)/q3static/ui_playermodel.o \
+ $(B)/q3static/ui_players.o \
+ $(B)/q3static/ui_playersettings.o \
+ $(B)/q3static/ui_preferences.o \
+ $(B)/q3static/ui_qmenu.o \
+ $(B)/q3static/ui_removebots.o \
+ $(B)/q3static/ui_saveconfig.o \
+ $(B)/q3static/ui_serverinfo.o \
+ $(B)/q3static/ui_servers2.o \
+ $(B)/q3static/ui_setup.o \
+ $(B)/q3static/ui_sound.o \
+ $(B)/q3static/ui_sparena.o \
+ $(B)/q3static/ui_specifyserver.o \
+ $(B)/q3static/ui_splevel.o \
+ $(B)/q3static/ui_sppostgame.o \
+ $(B)/q3static/ui_spskill.o \
+ $(B)/q3static/ui_startserver.o \
+ $(B)/q3static/ui_team.o \
+ $(B)/q3static/ui_teamorders.o \
+ $(B)/q3static/ui_video.o \
+ \
+ $(B)/q3static/ui_syscalls.o
+
+$(B)/q3static/ui_addbots.o : $(Q3UIDIR)/ui_addbots.c; $(DO_CC) -DQ3_STATIC
+$(B)/q3static/ui_atoms.o : $(Q3UIDIR)/ui_atoms.c; $(DO_CC) -DQ3_STATIC
+$(B)/q3static/ui_cinematics.o : $(Q3UIDIR)/ui_cinematics.c; $(DO_CC) -DQ3_STATIC
+$(B)/q3static/ui_cdkey.o : $(Q3UIDIR)/ui_cdkey.c; $(DO_CC) -DQ3_STATIC
+$(B)/q3static/ui_confirm.o : $(Q3UIDIR)/ui_confirm.c; $(DO_CC) -DQ3_STATIC
+$(B)/q3static/ui_connect.o : $(Q3UIDIR)/ui_connect.c; $(DO_CC) -DQ3_STATIC
+$(B)/q3static/ui_controls2.o : $(Q3UIDIR)/ui_controls2.c; $(DO_CC) -DQ3_STATIC
+$(B)/q3static/ui_credits.o : $(Q3UIDIR)/ui_credits.c; $(DO_CC) -DQ3_STATIC
+$(B)/q3static/ui_demo2.o : $(Q3UIDIR)/ui_demo2.c; $(DO_CC) -DQ3_STATIC
+$(B)/q3static/ui_display.o : $(Q3UIDIR)/ui_display.c; $(DO_CC) -DQ3_STATIC
+$(B)/q3static/ui_gameinfo.o : $(Q3UIDIR)/ui_gameinfo.c; $(DO_CC) -DQ3_STATIC
+$(B)/q3static/ui_ingame.o : $(Q3UIDIR)/ui_ingame.c; $(DO_CC) -DQ3_STATIC
+$(B)/q3static/ui_loadconfig.o : $(Q3UIDIR)/ui_loadconfig.c; $(DO_CC) -DQ3_STATIC
+$(B)/q3static/ui_main.o : $(Q3UIDIR)/ui_main.c; $(DO_CC) -DQ3_STATIC
+$(B)/q3static/ui_menu.o : $(Q3UIDIR)/ui_menu.c; $(DO_CC) -DQ3_STATIC
+$(B)/q3static/ui_mfield.o : $(Q3UIDIR)/ui_mfield.c; $(DO_CC) -DQ3_STATIC
+$(B)/q3static/ui_mods.o : $(Q3UIDIR)/ui_mods.c; $(DO_CC) -DQ3_STATIC
+$(B)/q3static/ui_network.o : $(Q3UIDIR)/ui_network.c; $(DO_CC) -DQ3_STATIC
+$(B)/q3static/ui_options.o : $(Q3UIDIR)/ui_options.c; $(DO_CC) -DQ3_STATIC
+$(B)/q3static/ui_playermodel.o : $(Q3UIDIR)/ui_playermodel.c; $(DO_CC) -DQ3_STATIC
+$(B)/q3static/ui_players.o : $(Q3UIDIR)/ui_players.c; $(DO_CC) -DQ3_STATIC
+$(B)/q3static/ui_playersettings.o : $(Q3UIDIR)/ui_playersettings.c; $(DO_CC) -DQ3_STATIC
+$(B)/q3static/ui_preferences.o : $(Q3UIDIR)/ui_preferences.c; $(DO_CC) -DQ3_STATIC
+$(B)/q3static/ui_qmenu.o : $(Q3UIDIR)/ui_qmenu.c; $(DO_CC) -DQ3_STATIC
+$(B)/q3static/ui_quit.o : $(Q3UIDIR)/ui_quit.c; $(DO_CC) -DQ3_STATIC
+$(B)/q3static/ui_removebots.o : $(Q3UIDIR)/ui_removebots.c; $(DO_CC) -DQ3_STATIC
+$(B)/q3static/ui_saveconfig.o : $(Q3UIDIR)/ui_saveconfig.c; $(DO_CC) -DQ3_STATIC
+$(B)/q3static/ui_serverinfo.o : $(Q3UIDIR)/ui_serverinfo.c; $(DO_CC) -DQ3_STATIC
+$(B)/q3static/ui_servers2.o : $(Q3UIDIR)/ui_servers2.c; $(DO_CC) -DQ3_STATIC
+$(B)/q3static/ui_setup.o : $(Q3UIDIR)/ui_setup.c; $(DO_CC) -DQ3_STATIC
+$(B)/q3static/ui_sound.o : $(Q3UIDIR)/ui_sound.c; $(DO_CC) -DQ3_STATIC
+$(B)/q3static/ui_sparena.o : $(Q3UIDIR)/ui_sparena.c; $(DO_CC) -DQ3_STATIC
+$(B)/q3static/ui_specifyserver.o : $(Q3UIDIR)/ui_specifyserver.c; $(DO_CC) -DQ3_STATIC
+$(B)/q3static/ui_splevel.o : $(Q3UIDIR)/ui_splevel.c; $(DO_CC) -DQ3_STATIC
+$(B)/q3static/ui_sppostgame.o : $(Q3UIDIR)/ui_sppostgame.c; $(DO_CC) -DQ3_STATIC
+$(B)/q3static/ui_spskill.o : $(Q3UIDIR)/ui_spskill.c; $(DO_CC) -DQ3_STATIC
+$(B)/q3static/ui_startserver.o : $(Q3UIDIR)/ui_startserver.c; $(DO_CC) -DQ3_STATIC
+$(B)/q3static/ui_team.o : $(Q3UIDIR)/ui_team.c; $(DO_CC) -DQ3_STATIC
+$(B)/q3static/ui_teamorders.o : $(Q3UIDIR)/ui_teamorders.c; $(DO_CC) -DQ3_STATIC
+$(B)/q3static/ui_syscalls.o : $(Q3UIDIR)/ui_syscalls.c; $(DO_CC) -DQ3_STATIC
+$(B)/q3static/ui_video.o : $(Q3UIDIR)/ui_video.c; $(DO_CC) -DQ3_STATIC
+
+
+## add shared files
+Q3SOBJ += \
+ $(B)/q3static/bg_misc.o \
+ $(B)/q3static/bg_pmove.o \
+ $(B)/q3static/bg_slidemove.o \
+ $(B)/q3static/q_math.o \
+ $(B)/q3static/q_shared.o
+
+## shared files
+$(B)/q3static/q_math.o : $(GDIR)/q_math.c; $(DO_CC) -DQ3_STATIC
+$(B)/q3static/q_shared.o : $(GDIR)/q_shared.c; $(DO_CC) -DQ3_STATIC
+$(B)/q3static/bg_misc.o : $(GDIR)/bg_misc.c; $(DO_CC) -DQ3_STATIC
+$(B)/q3static/bg_pmove.o : $(GDIR)/bg_pmove.c; $(DO_CC) -DQ3_STATIC
+$(B)/q3static/bg_slidemove.o : $(GDIR)/bg_slidemove.c; $(DO_CC) -DQ3_STATIC
+
+
+
+$(B)/$(PLATFORM)q3static : $(Q3SOBJ)
+ $(CC) $(CFLAGS) -o $@ $(Q3SOBJ) $(GLLDFLAGS) $(LDFLAGS)
+
+
+#############################################################################
+# RPM
+#############################################################################
+
+TMPDIR=/var/tmp
+TARDIR=$(TMPDIR)/$(BUILD_NAME)
+TARFILE = $(BUILD_NAME)-$(VERSION)-$(RPM_RELEASE).$(ARCH).tar
+
+tar:
+ if [ ! -d archives ];then mkdir archives;chmod 755 archives;fi
+ $(MAKE) copyfiles COPYDIR=$(TARDIR)
+ cd $(TARDIR); tar cvf $(TARFILE) * && gzip -9 $(TARFILE)
+ mv $(TARDIR)/$(TARFILE).gz archives/.
+ rm -rf $(TARDIR)
+
+# Make RPMs. You need to be root to make this work
+RPMROOT=/usr/src/redhat
+RPM = rpm
+RPMFLAGS = -bb
+INSTALLDIR = /usr/local/games/$(BUILD_NAME)
+RPMDIR = $(TMPDIR)/$(BUILD_NAME)-$(VERSION)
+DESTDIR= $(RPMDIR)/$(INSTALLDIR)
+
+rpm: $(BUILD_NAME).spec
+ touch $(RPMROOT)/SOURCES/$(BUILD_NAME)-$(VERSION).tar.gz
+ if [ ! -d archives ];then mkdir archives;fi
+ $(MAKE) copyfiles COPYDIR=$(DESTDIR)
+ cp $(UDIR)/quake3.gif $(RPMROOT)/SOURCES/.
+ cp $(BUILD_NAME).spec $(RPMROOT)/SPECS/.
+ cd $(RPMROOT)/SPECS; $(RPM) $(RPMFLAGS) $(BUILD_NAME).spec
+ rm -rf $(RPMDIR)
+ mv $(RPMROOT)/RPMS/$(RPMARCH)/$(BUILD_NAME)-$(VERSION)-$(RPM_RELEASE).$(RPMARCH).rpm archives/$(BUILD_NAME)-$(VERSION)-$(RPM_RELEASE).$(RPMARCH).rpm
+
+copyfiles:
+ -mkdirhier $(COPYDIR)
+ cp $(BR)/linuxquake3 $(COPYDIR)/quake3.x86
+ strip $(COPYDIR)/quake3.x86
+ chmod 755 $(COPYDIR)/quake3.x86
+ cp $(BR)/linuxq3ded $(COPYDIR)/q3ded
+ strip $(COPYDIR)/q3ded
+ chmod 755 $(COPYDIR)/q3ded
+ cp $(BDIR)/libMesaVoodooGL.so.3.2 $(COPYDIR)/.
+ chmod 755 $(COPYDIR)/libMesaVoodooGL.so.3.2
+ ( cd $(COPYDIR); ln -s libMesaVoodooGL.so.3.2 libMesaVoodooGL.so )
+ cp $(BDIR)/Quake_III_Arena_FAQ.html $(COPYDIR)/.
+ chmod 644 $(COPYDIR)/Quake_III_Arena_FAQ.html
+ mkdir $(COPYDIR)/baseq3
+ cp $(BASEQ3_DIR)/pak2.pk3 $(COPYDIR)/baseq3/.
+ chmod 644 $(COPYDIR)/baseq3/pak2.pk3
+
+$(BUILD_NAME).spec : $(UDIR)/$(BUILD_NAME).spec.sh Makefile
+ sh $< $(VERSION) $(RPM_RELEASE) $(ARCH) $(INSTALLDIR) > $@
+
+#############################################################################
+# MISC
+#############################################################################
+
+# TTimo: FIXME: doesn't clean the binary and .so
+
+clean:clean-debug clean-release
+
+clean2: clean-bins
+ rm -f $(Q3OBJ) $(Q3POBJ) $(Q3POBJ_SMP) $(Q3DOBJ) $(MPGOBJ) $(Q3GOBJ) $(Q3CGOBJ) $(MPCGOBJ) $(Q3UIOBJ) $(MPUIOBJ)
+ rm -f $(CGDIR)/vm/*.asm
+ rm -f $(GDIR)/vm/*.asm
+ rm -f $(UIDIR)/vm/*.asm
+ rm -f $(Q3UIDIR)/vm/*.asm
+
+# TTimo: linuxq3ded linuxquake3 linuxquake3-smp .. hardcoded the names .. maybe not such a good thing
+# FIXME: also, removing the *.so is crappy .. I just want to avoid rm -rf debugi386-glibc to save the symlinks to pk3's for testing
+clean-bins:
+ if [ -d $(B) ];then (find $(B) -name '*.so' -exec rm {} \;)fi
+ rm -f $(B)/linuxq3ded
+ rm -f $(B)/linuxquake3
+ rm -f $(B)/linuxquake3-smp
+ rm -f $(B)/baseq3/vm/cgame.qvm
+ rm -f $(B)/baseq3/vm/ui.qvm
+ rm -f $(B)/baseq3/vm/qagame.qvm
+ rm -f $(B)/missionpack/vm/cgame.qvm
+ rm -f $(B)/missionpack/vm/qagame.qvm
+ rm -f $(B)/missionpack/vm/ui.qvm
+
+clean-debug:
+ $(MAKE) clean2 B=$(BD) CFLAGS="$(DEBUG_CFLAGS)"
+
+clean-release:
+ $(MAKE) clean2 B=$(BR) CFLAGS="$(DEBUG_CFLAGS)"
+
+# TTimo: for the setups it's easier to shell out to a script
+
+setup: release
+ ./build_setup.sh $(VERSION)
+
diff --git a/code/unix/Makefile.Game b/code/unix/Makefile.Game
new file mode 100755
index 0000000..8da0219
--- /dev/null
+++ b/code/unix/Makefile.Game
@@ -0,0 +1,285 @@
+#
+# Quake3 Unix Game Makefile
+#
+# GNU Make required
+#
+
+###
+### These paths are where you probably want to change things
+###
+
+#The main Quake3 directory
+BDIR=..
+
+#This is the game dir
+GAMEDIR=baseq3
+
+#Where the source is, assumed to be same directory as this Makefile
+SRCDIR=.
+
+#Location of binaries
+BINDIR=../bin
+LCC=$(BINDIR)/lcc # -lccdir=$(BINDIR)/
+Q3ASM=$(BINDIR)/q3asm
+
+
+#############################################################################
+##
+## You shouldn't have to touch anything below here
+##
+#############################################################################
+
+GDIR=$(SRCDIR)/game
+CGDIR=$(SRCDIR)/cgame
+UIDIR=$(SRCDIR)/ui
+
+LCCFLAGS=-DQ3_VM -S -Wf-target=bytecode -Wf-g -I..\cgame -I..\game -I..\ui
+
+DO_LCC=$(LCC) $(LCCFLAGS) -o $@ -c $<
+
+TARGETS=\
+ $(BDIR)/$(GAMEDIR)/vm/cgame.qvm \
+ $(BDIR)/$(GAMEDIR)/vm/qagame.qvm \
+ $(BDIR)/$(GAMEDIR)/vm/ui.qvm
+
+default: makedir $(TARGETS)
+
+makedir:
+ @-mkdir $(GDIR)/vm
+ @-mkdir $(CGDIR)/vm
+ @-mkdir $(UIDIR)/vm
+
+#############################################################################
+# GAME VM
+#############################################################################
+
+##NOTE: g_main must be first. Control passes to first function in the vm
+
+GVMASM = \
+ $(GDIR)/vm/g_main.asm \
+ $(GDIR)/vm/bg_misc.asm \
+ $(GDIR)/vm/bg_lib.asm \
+ $(GDIR)/vm/bg_pmove.asm \
+ $(GDIR)/vm/bg_slidemove.asm \
+ $(GDIR)/vm/q_math.asm \
+ $(GDIR)/vm/q_shared.asm \
+ $(GDIR)/vm/ai_dmnet.asm \
+ $(GDIR)/vm/ai_dmq3.asm \
+ $(GDIR)/vm/ai_team.asm \
+ $(GDIR)/vm/ai_main.asm \
+ $(GDIR)/vm/ai_chat.asm \
+ $(GDIR)/vm/ai_cmd.asm \
+ $(GDIR)/vm/g_active.asm \
+ $(GDIR)/vm/g_arenas.asm \
+ $(GDIR)/vm/g_bot.asm \
+ $(GDIR)/vm/g_client.asm \
+ $(GDIR)/vm/g_cmds.asm \
+ $(GDIR)/vm/g_combat.asm \
+ $(GDIR)/vm/g_items.asm \
+ $(GDIR)/vm/g_mem.asm \
+ $(GDIR)/vm/g_misc.asm \
+ $(GDIR)/vm/g_missile.asm \
+ $(GDIR)/vm/g_mover.asm \
+ $(GDIR)/vm/g_session.asm \
+ $(GDIR)/vm/g_spawn.asm \
+ $(GDIR)/vm/g_svcmds.asm \
+ $(GDIR)/vm/g_target.asm \
+ $(GDIR)/vm/g_team.asm \
+ $(GDIR)/vm/g_trigger.asm \
+ $(GDIR)/vm/g_utils.asm \
+ $(GDIR)/vm/g_weapon.asm
+
+$(BDIR)/$(GAMEDIR)/vm/qagame.qvm : $(GVMASM)
+ $(Q3ASM) -o $@ $(GVMASM) $(SRCDIR)/game/g_syscalls.asm
+
+$(GDIR)/vm/ai_chat.asm : $(GDIR)/ai_chat.c; $(DO_LCC)
+$(GDIR)/vm/ai_cmd.asm : $(GDIR)/ai_cmd.c; $(DO_LCC)
+$(GDIR)/vm/ai_dmnet.asm : $(GDIR)/ai_dmnet.c; $(DO_LCC)
+$(GDIR)/vm/ai_dmq3.asm : $(GDIR)/ai_dmq3.c; $(DO_LCC)
+$(GDIR)/vm/ai_main.asm : $(GDIR)/ai_main.c; $(DO_LCC)
+$(GDIR)/vm/ai_team.asm : $(GDIR)/ai_team.c; $(DO_LCC)
+$(GDIR)/vm/bg_lib.asm : $(GDIR)/bg_lib.c; $(DO_LCC)
+$(GDIR)/vm/bg_misc.asm : $(GDIR)/bg_misc.c; $(DO_LCC)
+$(GDIR)/vm/bg_pmove.asm : $(GDIR)/bg_pmove.c; $(DO_LCC)
+$(GDIR)/vm/bg_slidemove.asm : $(GDIR)/bg_slidemove.c; $(DO_LCC)
+$(GDIR)/vm/g_active.asm : $(GDIR)/g_active.c; $(DO_LCC)
+$(GDIR)/vm/g_arenas.asm : $(GDIR)/g_arenas.c; $(DO_LCC)
+$(GDIR)/vm/g_bot.asm : $(GDIR)/g_bot.c; $(DO_LCC)
+$(GDIR)/vm/g_client.asm : $(GDIR)/g_client.c; $(DO_LCC)
+$(GDIR)/vm/g_cmds.asm : $(GDIR)/g_cmds.c; $(DO_LCC)
+$(GDIR)/vm/g_combat.asm : $(GDIR)/g_combat.c; $(DO_LCC)
+$(GDIR)/vm/g_items.asm : $(GDIR)/g_items.c; $(DO_LCC)
+$(GDIR)/vm/g_main.asm : $(GDIR)/g_main.c; $(DO_LCC)
+$(GDIR)/vm/g_mem.asm : $(GDIR)/g_mem.c; $(DO_LCC)
+$(GDIR)/vm/g_misc.asm : $(GDIR)/g_misc.c; $(DO_LCC)
+$(GDIR)/vm/g_missile.asm : $(GDIR)/g_missile.c; $(DO_LCC)
+$(GDIR)/vm/g_mover.asm : $(GDIR)/g_mover.c; $(DO_LCC)
+$(GDIR)/vm/g_session.asm : $(GDIR)/g_session.c; $(DO_LCC)
+$(GDIR)/vm/g_spawn.asm : $(GDIR)/g_spawn.c; $(DO_LCC)
+$(GDIR)/vm/g_svcmds.asm : $(GDIR)/g_svcmds.c; $(DO_LCC)
+$(GDIR)/vm/g_target.asm : $(GDIR)/g_target.c; $(DO_LCC)
+$(GDIR)/vm/g_team.asm : $(GDIR)/g_team.c; $(DO_LCC)
+$(GDIR)/vm/g_trigger.asm : $(GDIR)/g_trigger.c; $(DO_LCC)
+$(GDIR)/vm/g_utils.asm : $(GDIR)/g_utils.c; $(DO_LCC)
+$(GDIR)/vm/g_weapon.asm : $(GDIR)/g_weapon.c; $(DO_LCC)
+$(GDIR)/vm/q_math.asm : $(GDIR)/q_math.c; $(DO_LCC)
+$(GDIR)/vm/q_shared.asm : $(GDIR)/q_shared.c; $(DO_LCC)
+
+$(BDIR)/$(GAMEDIR)/vm/cgame.qvm :
+ echo cgame.qvm disabled
+
+$(BDIR)/$(GAMEDIR)/vm/ui.qvm :
+ echo ui.qvm disabled
+
+#############################################################################
+# CGAME
+#############################################################################
+
+CGOBJ = \
+ $(SRCDIR)/cgame/bg_lib.o \
+ $(SRCDIR)/cgame/bg_misc.o \
+ $(SRCDIR)/cgame/bg_pmove.o \
+ $(SRCDIR)/cgame/bg_slidemove.o \
+ $(SRCDIR)/cgame/cg_consolecmds.o \
+ $(SRCDIR)/cgame/cg_draw.o \
+ $(SRCDIR)/cgame/cg_drawtools.o \
+ $(SRCDIR)/cgame/cg_effects.o \
+ $(SRCDIR)/cgame/cg_ents.o \
+ $(SRCDIR)/cgame/cg_event.o \
+ $(SRCDIR)/cgame/cg_info.o \
+ $(SRCDIR)/cgame/cg_localents.o \
+ $(SRCDIR)/cgame/cg_main.o \
+ $(SRCDIR)/cgame/cg_marks.o \
+ $(SRCDIR)/cgame/cg_players.o \
+ $(SRCDIR)/cgame/cg_playerstate.o \
+ $(SRCDIR)/cgame/cg_predict.o \
+ $(SRCDIR)/cgame/cg_scoreboard.o \
+ $(SRCDIR)/cgame/cg_servercmds.o \
+ $(SRCDIR)/cgame/cg_snapshot.o \
+ $(SRCDIR)/cgame/cg_view.o \
+ $(SRCDIR)/cgame/cg_weapons.o \
+ $(SRCDIR)/cgame/q_math.o \
+ $(SRCDIR)/cgame/q_shared.o
+
+$(SRCDIR)/cgame$(ARCH).$(SHLIBEXT) : $(CGOBJ)
+ $(CC) $(CFLAGS) $(SHLIBLDFLAGS) -o $@ $(CGOBJ)
+
+$(SRCDIR)/cgame/bg_lib.o : $(GDIR)/bg_lib.c; $(DO_SHLIB_CC)
+$(SRCDIR)/cgame/bg_misc.o : $(GDIR)/bg_misc.c; $(DO_SHLIB_CC)
+$(SRCDIR)/cgame/bg_pmove.o : $(GDIR)/bg_pmove.c; $(DO_SHLIB_CC)
+$(SRCDIR)/cgame/bg_slidemove.o : $(GDIR)/bg_slidemove.c; $(DO_SHLIB_CC)
+$(SRCDIR)/cgame/cg_consolecmds.o : $(CGDIR)/cg_consolecmds.c; $(DO_SHLIB_CC)
+$(SRCDIR)/cgame/cg_draw.o : $(CGDIR)/cg_draw.c; $(DO_SHLIB_CC)
+$(SRCDIR)/cgame/cg_drawtools.o : $(CGDIR)/cg_drawtools.c; $(DO_SHLIB_CC)
+$(SRCDIR)/cgame/cg_effects.o : $(CGDIR)/cg_effects.c; $(DO_SHLIB_CC)
+$(SRCDIR)/cgame/cg_ents.o : $(CGDIR)/cg_ents.c; $(DO_SHLIB_CC)
+$(SRCDIR)/cgame/cg_event.o : $(CGDIR)/cg_event.c; $(DO_SHLIB_CC)
+$(SRCDIR)/cgame/cg_info.o : $(CGDIR)/cg_info.c; $(DO_SHLIB_CC)
+$(SRCDIR)/cgame/cg_localents.o : $(CGDIR)/cg_localents.c; $(DO_SHLIB_CC)
+$(SRCDIR)/cgame/cg_main.o : $(CGDIR)/cg_main.c; $(DO_SHLIB_CC)
+$(SRCDIR)/cgame/cg_marks.o : $(CGDIR)/cg_marks.c; $(DO_SHLIB_CC)
+$(SRCDIR)/cgame/cg_players.o : $(CGDIR)/cg_players.c; $(DO_SHLIB_CC)
+$(SRCDIR)/cgame/cg_playerstate.o : $(CGDIR)/cg_playerstate.c; $(DO_SHLIB_CC)
+$(SRCDIR)/cgame/cg_predict.o : $(CGDIR)/cg_predict.c; $(DO_SHLIB_CC)
+$(SRCDIR)/cgame/cg_scoreboard.o : $(CGDIR)/cg_scoreboard.c; $(DO_SHLIB_CC)
+$(SRCDIR)/cgame/cg_servercmds.o : $(CGDIR)/cg_servercmds.c; $(DO_SHLIB_CC)
+$(SRCDIR)/cgame/cg_snapshot.o : $(CGDIR)/cg_snapshot.c; $(DO_SHLIB_CC)
+$(SRCDIR)/cgame/cg_view.o : $(CGDIR)/cg_view.c; $(DO_SHLIB_CC)
+$(SRCDIR)/cgame/cg_weapons.o : $(CGDIR)/cg_weapons.c; $(DO_SHLIB_CC)
+$(SRCDIR)/cgame/q_math.o : $(GDIR)/q_math.c; $(DO_SHLIB_CC)
+$(SRCDIR)/cgame/q_shared.o : $(GDIR)/q_shared.c; $(DO_SHLIB_CC)
+
+#############################################################################
+# UI
+#############################################################################
+
+UIOBJ = \
+ $(SRCDIR)/ui/bg_lib.o \
+ $(SRCDIR)/ui/bg_misc.o \
+ $(SRCDIR)/ui/ui_addbots.o \
+ $(SRCDIR)/ui/ui_atoms.o \
+ $(SRCDIR)/ui/ui_cinematics.o \
+ $(SRCDIR)/ui/ui_confirm.o \
+ $(SRCDIR)/ui/ui_connect.o \
+ $(SRCDIR)/ui/ui_controls2.o \
+ $(SRCDIR)/ui/ui_credits.o \
+ $(SRCDIR)/ui/ui_demo2.o \
+ $(SRCDIR)/ui/ui_display.o \
+ $(SRCDIR)/ui/ui_gameinfo.o \
+ $(SRCDIR)/ui/ui_ingame.o \
+ $(SRCDIR)/ui/ui_loadconfig.o \
+ $(SRCDIR)/ui/ui_main.o \
+ $(SRCDIR)/ui/ui_menu.o \
+ $(SRCDIR)/ui/ui_mfield.o \
+ $(SRCDIR)/ui/ui_network.o \
+ $(SRCDIR)/ui/ui_playermodel.o \
+ $(SRCDIR)/ui/ui_players.o \
+ $(SRCDIR)/ui/ui_playersettings.o \
+ $(SRCDIR)/ui/ui_preferences.o \
+ $(SRCDIR)/ui/ui_qmenu.o \
+ $(SRCDIR)/ui/ui_saveconfig.o \
+ $(SRCDIR)/ui/ui_serverinfo.o \
+ $(SRCDIR)/ui/ui_servers2.o \
+ $(SRCDIR)/ui/ui_setup.o \
+ $(SRCDIR)/ui/ui_sound.o \
+ $(SRCDIR)/ui/ui_sparena.o \
+ $(SRCDIR)/ui/ui_specifyserver.o \
+ $(SRCDIR)/ui/ui_splevel.o \
+ $(SRCDIR)/ui/ui_sppostgame.o \
+ $(SRCDIR)/ui/ui_spskill.o \
+ $(SRCDIR)/ui/ui_startserver.o \
+ $(SRCDIR)/ui/ui_team.o \
+ $(SRCDIR)/ui/ui_video.o \
+ \
+ $(SRCDIR)/ui/q_math.o \
+ $(SRCDIR)/ui/q_shared.o
+
+$(SRCDIR)/ui$(ARCH).$(SHLIBEXT) : $(UIOBJ)
+ $(CC) $(CFLAGS) $(SHLIBLDFLAGS) -o $@ $(UIOBJ)
+
+$(SRCDIR)/ui/bg_lib.o : $(GDIR)/bg_lib.c; $(DO_CC)
+$(SRCDIR)/ui/bg_misc.o : $(GDIR)/bg_misc.c; $(DO_CC)
+$(SRCDIR)/ui/ui_addbots.o : $(UIDIR)/ui_addbots.c; $(DO_CC)
+$(SRCDIR)/ui/ui_atoms.o : $(UIDIR)/ui_atoms.c; $(DO_CC)
+$(SRCDIR)/ui/ui_cinematics.o : $(UIDIR)/ui_cinematics.c; $(DO_CC)
+$(SRCDIR)/ui/ui_confirm.o : $(UIDIR)/ui_confirm.c; $(DO_CC)
+$(SRCDIR)/ui/ui_connect.o : $(UIDIR)/ui_connect.c; $(DO_CC)
+$(SRCDIR)/ui/ui_controls2.o : $(UIDIR)/ui_controls2.c; $(DO_CC)
+$(SRCDIR)/ui/ui_credits.o : $(UIDIR)/ui_credits.c; $(DO_CC)
+$(SRCDIR)/ui/ui_demo2.o : $(UIDIR)/ui_demo2.c; $(DO_CC)
+$(SRCDIR)/ui/ui_display.o : $(UIDIR)/ui_display.c; $(DO_CC)
+$(SRCDIR)/ui/ui_gameinfo.o : $(UIDIR)/ui_gameinfo.c; $(DO_CC)
+$(SRCDIR)/ui/ui_ingame.o : $(UIDIR)/ui_ingame.c; $(DO_CC)
+$(SRCDIR)/ui/ui_loadconfig.o : $(UIDIR)/ui_loadconfig.c; $(DO_CC)
+$(SRCDIR)/ui/ui_main.o : $(UIDIR)/ui_main.c; $(DO_CC)
+$(SRCDIR)/ui/ui_menu.o : $(UIDIR)/ui_menu.c; $(DO_CC)
+$(SRCDIR)/ui/ui_mfield.o : $(UIDIR)/ui_mfield.c; $(DO_CC)
+$(SRCDIR)/ui/ui_network.o : $(UIDIR)/ui_network.c; $(DO_CC)
+$(SRCDIR)/ui/ui_playermodel.o : $(UIDIR)/ui_playermodel.c; $(DO_CC)
+$(SRCDIR)/ui/ui_players.o : $(UIDIR)/ui_players.c; $(DO_CC)
+$(SRCDIR)/ui/ui_playersettings.o : $(UIDIR)/ui_playersettings.c; $(DO_CC)
+$(SRCDIR)/ui/ui_preferences.o : $(UIDIR)/ui_preferences.c; $(DO_CC)
+$(SRCDIR)/ui/ui_qmenu.o : $(UIDIR)/ui_qmenu.c; $(DO_CC)
+$(SRCDIR)/ui/ui_quit.o : $(UIDIR)/ui_quit.c; $(DO_CC)
+$(SRCDIR)/ui/ui_saveconfig.o : $(UIDIR)/ui_saveconfig.c; $(DO_CC)
+$(SRCDIR)/ui/ui_serverinfo.o : $(UIDIR)/ui_serverinfo.c; $(DO_CC)
+$(SRCDIR)/ui/ui_servers2.o : $(UIDIR)/ui_servers2.c; $(DO_CC)
+$(SRCDIR)/ui/ui_setup.o : $(UIDIR)/ui_setup.c; $(DO_CC)
+$(SRCDIR)/ui/ui_sound.o : $(UIDIR)/ui_sound.c; $(DO_CC)
+$(SRCDIR)/ui/ui_sparena.o : $(UIDIR)/ui_sparena.c; $(DO_CC)
+$(SRCDIR)/ui/ui_specifyserver.o : $(UIDIR)/ui_specifyserver.c; $(DO_CC)
+$(SRCDIR)/ui/ui_splevel.o : $(UIDIR)/ui_splevel.c; $(DO_CC)
+$(SRCDIR)/ui/ui_sppostgame.o : $(UIDIR)/ui_sppostgame.c; $(DO_CC)
+$(SRCDIR)/ui/ui_spskill.o : $(UIDIR)/ui_spskill.c; $(DO_CC)
+$(SRCDIR)/ui/ui_startserver.o : $(UIDIR)/ui_startserver.c; $(DO_CC)
+$(SRCDIR)/ui/ui_team.o : $(UIDIR)/ui_team.c; $(DO_CC)
+$(SRCDIR)/ui/ui_video.o : $(UIDIR)/ui_video.c; $(DO_CC)
+$(SRCDIR)/ui/q_math.o : $(GDIR)/q_math.c; $(DO_SHLIB_CC)
+$(SRCDIR)/ui/q_shared.o : $(GDIR)/q_shared.c; $(DO_SHLIB_CC)
+
+#############################################################################
+# MISC
+#############################################################################
+
+clean:
+ rm -f $(GVMASM)
diff --git a/code/unix/Quake3.kdelnk b/code/unix/Quake3.kdelnk
new file mode 100755
index 0000000..a4327ce
--- /dev/null
+++ b/code/unix/Quake3.kdelnk
@@ -0,0 +1,12 @@
+# KDE Config File
+[KDE Desktop Entry]
+Name=Quake3
+Name[C]=Quake3
+MimeType=
+Exec=/usr/local/games/quake3/linuxquake3
+Comment=Quake3: Arena by id Software
+Icon=quake3.xpm
+TerminalOptions=
+Path=
+Type=Application
+Terminal=0
diff --git a/code/unix/README.EULA b/code/unix/README.EULA
new file mode 100755
index 0000000..9b28721
--- /dev/null
+++ b/code/unix/README.EULA
@@ -0,0 +1,184 @@
+ LIMITED COMPATIBILITY TESTING AND RECREATIONAL
+ USE SOFTWARE TEST LICENSE AGREEMENT
+
+This Limited Compatibility Testing and Recreational Use Software Test
+License Agreement (the "Agreement") is a legal agreement between you, the
+end-user, and Id Software, Inc. ("ID"). BY CONTINUING THE INSTALLATION
+OF THIS TEST VERSION OF THE GAME PROGRAM ENTITLED QUAKE III: ARENA (THE
+"SOFTWARE"), BY LOADING OR RUNNING THE SOFTWARE, OR BY PLACING OR COPYING
+THE SOFTWARE ONTO YOUR COMPUTER HARD DRIVE, COMPUTER RAM OR OTHER STORAGE,
+YOU ARE AGREEING TO BE BOUND BY THE TERMS OF THIS AGREEMENT.
+
+1. Grant of License. Subject to the terms and provisions of this
+Agreement, ID grants to you the non-exclusive limited right to use this
+Software only in executable or object code form and only for the purpose
+of testing the compatibility of your computer system with the Software
+and for non-commercial, recreational purposes. The term "Software"
+includes all elements of the Software such as data files and screen
+displays. You are not receiving any ownership or proprietary right,
+title or interest in or to the Software or the copyright, trademarks, or
+other rights related thereto. For purposes of this section, "use" means
+loading the Software into RAM and/or onto computer hard drive, as well
+as installation of the Software on a hard disk or other storage device.
+You agree that the Software will not be shipped, transferred or exported
+into any country in violation of the U.S. Export Administration Act (or
+any other law governing such matters) by you or anyone at your direction
+and that you will not utilize and will not authorize anyone to utilize,
+in any other manner, the Software in violation of any applicable law.
+The Software may not be downloaded or otherwise exported or reexported
+into (or to a national or resident of) any country to which the U.S. has
+embargoed goods or to anyone or into any country who/which are prohibited,
+by applicable law, from receiving such property.
+
+2. Prohibitions. You, either directly or indirectly, shall not do
+any of the following acts:
+
+a. rent the Software;
+
+b. sell the Software;
+
+c. lease or lend the Software;
+
+d. offer the Software on a "pay-per-play" basis;
+
+e. distribute the Software (except by electronic means, as permitted
+by section 3. hereinbelow) by any means, including, but not limited to
+direct mail, retail, mail order or other means;
+
+f. in any other manner and through any medium whatsoever commercially
+exploit the Software or use the Software for any commercial purpose;
+
+g. disassemble, reverse engineer, disassemble, decompile, modify
+or alter the Software;
+
+h. translate the Software;
+
+i. reproduce or copy the Software (except as permitted by section
+3. hereinbelow);
+
+j. publicly display the Software;
+
+k. prepare or develop derivative works based upon the Software;
+
+l. remove or alter any legal notices, such as trademark and copyright
+notices, affixed on or within the Software; or
+
+m. create or develop extra or add-on levels for the Software.
+
+3. Permitted Electronic Distribution and Copying. So long as this
+Agreement accompanies the Software at all times, ID grants to you the
+limited right to distribute, free of charge, and by electronic means
+only, the Software. Anyone who receives the Software shall be limited to
+all the terms and conditions of this Agreement. You may make only the
+following copies of the Software: (i) you may download the Software
+from the Internet and onto your computer hard drive; (ii) you may copy
+the Software from your computer hard drive into your computer RAM; and
+(iii) you may make one (1) "back up" or archival copy of the Software
+on one (1) hard disk.
+
+4. Copyright. The Software and all copyrights, trademarks and all
+other conceivable intellectual property rights related to the Software
+are owned by ID and are protected by United States copyright laws,
+international treaty provisions and all applicable law, such as the
+Lanham Act. You must treat the Software like any other copyrighted
+material, as required by 17 U.S.C., ¤101 et seq. and other applicable law.
+You agree to use your best efforts to see that any user of the Software
+licensed hereunder complies with this Agreement. You agree that you
+are receiving a copy of the Software by license only and not by sale and
+that the "first sale" doctrine of 17 U.S.C. ¤109 does not apply to your
+receipt or use of the Software.
+
+5. NO WARRANTIES. ID DISCLAIMS ALL WARRANTIES, WHETHER EXPRESS
+OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, IMPLIED WARRANTIES OF
+MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE WITH RESPECT TO
+THE SOFTWARE. ID DOES NOT WARRANT THAT THE OPERATION OF THE SOFTWARE
+WILL BE UNINTERRUPTED OR ERROR FREE OR THAT THE SOFTWARE WILL MEET YOUR
+SPECIFIC REQUIREMENTS. ADDITIONAL STATEMENTS SUCH AS PRESENTATIONS,
+WHETHER ORAL OR WRITTEN, DO NOT CONSTITUTE WARRANTIES BY ID AND SHOULD
+NOT BE RELIED UPON.
+
+6. Governing Law, Venue and Liability Limitation. This Agreement
+shall be construed in accordance with and governed by the applicable laws
+of the State of Texas and applicable United States federal law. Copyright
+and other proprietary matters will be governed by United States laws and
+international treaties. Exclusive venue for all litigation regarding
+this Agreement shall be in Dallas County, Texas and you agree to submit to
+the jurisdiction of the courts in Dallas, Texas for any such litigation.
+IN ANY CASE, NEITHER ID NOR ID'S OFFICERS, EMPLOYEES, DIRECTORS, AGENTS,
+LICENSEES, SUBLICENSEES, SUCCESSORS OR ASSIGNS SHALL BE LIABLE FOR LOSS OF
+DATA, LOSS OF PROFITS, LOST SAVINGS, SPECIAL, INCIDENTAL, CONSEQUENTIAL,
+INDIRECT, PUNITIVE OR OTHER SIMILAR DAMAGES ARISING FROM BREACH OF
+WARRANTY, BREACH OF CONTRACT, NEGLIGENCE, STRICT PRODUCT LIABILITY,
+OR OTHER LEGAL THEORY EVEN IF ID OR ITS AGENT HAS BEEN ADVISED OF THE
+POSSIBILITY OF SUCH DAMAGES OR EVEN IF SUCH DAMAGES ARE FORESEEABLE,
+OR LIABLE FOR ANY CLAIM BY ANY OTHER PARTY. Some jurisdictions do not
+allow the exclusion or limitation of incidental or consequential damages,
+so the above limitation or exclusion may not apply to you.
+
+7. U.S. Government Restricted Rights. To the extent applicable,
+the United States Government shall only have those rights to use the
+Software as expressly stated and expressly limited and restricted in
+this Agreement, as provided in 48 C.F.R. ¤¤ 227.7202-1 through 227.7204,
+inclusive.
+
+8. General Provisions. A copy of all notices or other correspondence
+which you send to ID shall also be sent by you to ID's counsel:
+
+D. Wade Cloud, Jr.
+HIERSCHE, MARTENS, HAYWARD, DRAKELEY & URBACH, P.C.
+15303 Dallas Parkway, Suite 700
+Dallas, TX 75248
+(972) 701-7000
+Facsimile: (972) 701-8765
+
+Neither this Agreement nor any part or portion hereof shall be assigned
+or sublicensed by you. ID may assign its rights under this Agreement
+in ID's sole discretion. Should any provision of this Agreement
+be held to be void, invalid, unenforceable or illegal by a court,
+the validity and enforceability of the other provisions shall not be
+affected thereby. If any provision is determined to be unenforceable,
+you agree to a modification of such provision to provide for enforcement
+of the provision's intent, to the extent permitted by applicable law.
+Failure of ID to enforce any provision of this Agreement shall not
+constitute or be construed as a waiver of such provision or of the right
+to enforce such provision. If you fail to comply with any term of this
+Agreement, YOUR LICENSE IS AUTOMATICALLY TERMINATED, WITHOUT NOTICE AND
+ID MAY PURSUE ALL RELIEF AND REMEDIES AGAINST YOU WHICH ARE AVAILABLE
+UNDER APPLICABLE LAW AND/OR THIS AGREEMENT. You agree that in the event
+of litigation relating to this Agreement, the prevailing litigant shall
+be entitled to recover the prevailing litigant's attorneys' fees and
+expenses and costs of court in addition to all other relief available
+under this Agreement and/or applicable law. In the event this Agreement
+is terminated, you shall have no right to use the Software, in any manner
+and you shall immediately destroy all copies of the Software in your
+possession, custody or control. You agree that your unauthorized use
+of any ID property, whether in whole or in part, would immediately and
+irreparably damage ID such that ID could not be adequately compensated
+by an award of monetary damages, and in the event of such threatened
+or actual unauthorized use ID shall be entitled to an injunctive order
+appropriately restraining and/or prohibiting such unauthorized use
+without the necessity of ID posting bond or other security.
+
+YOU ACKNOWLEDGE THAT YOU HAVE READ THIS AGREEMENT, YOU UNDERSTAND THIS
+AGREEMENT, AND UNDERSTAND THAT BY CONTINUING THE INSTALLATION OF THE
+SOFTWARE, BY LOADING OR RUNNING THE SOFTWARE, OR BY PLACING OR COPYING
+THE SOFTWARE ONTO YOUR COMPUTER HARD DRIVE OR RAM, YOU AGREE TO BE BOUND
+BY THE TERMS AND CONDITIONS OF THIS AGREEMENT. YOU FURTHER AGREE THAT,
+EXCEPT FOR WRITTEN SEPARATE AGREEMENTS BETWEEN ID AND YOU, THIS AGREEMENT
+IS A COMPLETE AND EXCLUSIVE STATEMENT OF THE RIGHTS AND LIABILITIES OF
+THE PARTIES HERETO. THIS AGREEMENT SUPERSEDES ALL PRIOR ORAL AGREEMENTS,
+PROPOSALS OR UNDERSTANDINGS, AND ANY OTHER COMMUNICATIONS BETWEEN ID
+AND YOU RELATING TO THE SUBJECT MATTER OF THIS AGREEMENT.
+
+
+
+April 23, 1999 5:11 p.m.
+
+
+
+
+
+LIMITED COMPATIBILITY TESTING AND RECREATIONAL USE SOFTWARE
+TEST LICENSE AGREEMENT Page 4
+3406.0586\C:\DWC\DOC\2082.DOC
+
diff --git a/code/unix/README.Linux b/code/unix/README.Linux
new file mode 100755
index 0000000..ef3b4c5
--- /dev/null
+++ b/code/unix/README.Linux
@@ -0,0 +1,352 @@
++----------------------------------------+
+| Q3Test |
+| The Official Quake 3: Arena TEST |
+| Linux version |
+| Revision 1.08 |
+| Released Aug 3, 1999 |
+| Copyright (C) 1999 id Software, inc. |
++----------------------------------------+
+| Documentation: Paul Jaquays |
+| Dave Kirsch |
++----------------------------------------+
+
+Outline:
+
+1. Introduction
+2. Bug Reporting
+3. Installing Q3Test
+4. Running Q3Test. Operating Instructions
+5. Known Issues
+
+==================
+== Section 1. ==
+== INTRODUCTION ==
+==================
+
+This is the Linux version of the test.
+
+Before proceeding onward, please read the README.EULA file. This is
+the End User License Agreement. It's the agreement between you and id
+regarding this test. Read it. Understand that the acts of loading or
+running the software, or placing it on your computer hard drive, RAM,
+or other storage, you are agreeing to be bound by the terms of the End
+User License Agreement. So it's best to know what you are agreeing to.
+
+The operating instructions for the test are included in the
+separate "README.Q3Test" file. For the most part, the game functions
+are the same across all platforms. Differences will be noted when necessary.
+
++----------------+
+| THIS IS A TEST |
++----------------+
+
+We want to set the record straight from the outset. This is neither a
+demo nor an ALPHA version of the final game and should not be expected
+to behave as such. Q3Test is a test of Quake3: Arena's network technology
+and graphics compatibility. While many of the game features that you will
+see in this test are in a highly polished state, do not confuse this with
+being anything other than what it is intended to be: a test. By running
+this test openly on the Internet and on a multitude of local networks,
+we hope to expose problems that could not otherwise be discovered on the
+limited number of in-house systems and computer. That's why we need your
+assistance for this test.
+
+Furthermore, it is anticipated that there may be revisions and additions
+made to the test that will make it incompatible with earlier versions.
+
++---------------------+
+| SYSTEM REQUIREMENTS |
++---------------------+
+
+There is no software-only version of the game. If your computer is not
+hardware accelerated with a game compatible graphics card, you will NOT be
+able to run Q3Test. Currently, 3DFX based cards are the only
+tested compatible cards. If your computer does not meet the
+minimum requirements, you are unlikely to be able to run a satisfactory
+game experience.
+
+A glibc compatible Linux installation is required. An easy to determine
+if you have glibc support is to type this:
+
+ ls -l /lib/libc*
+
+If you get a report of libc6 (you may also have libc5), you have a
+glibc based system.
+
+The Linux version requires a supported hardware 3D acceleration card. The
+following are currently supported:
+
+ o 3Dfx Voodoo Graphics based cards
+ o 3Dfx Voodoo2 based cards
+
+Other cards of similar make and model may work, but are not supported.
+
+Other X11 OpenGL libraries may work with a supported hardware
+accelerator, but have not been extensively tested. Performance on
+untested OpenGL configurations may not produce acceptable game
+speed.
+
+For 3Dfx based products, Linux Q3Test is distributed with a version of Mesa
+3-D specifically compiled for Voodoo support. You must have 3Dfx Linux Glide
+drivers installed however. These drivers are available from:
+
+ http://glide.xxedgexx.com/3DfxRPMS.html
+
+Make sure that you download and install the glibc versions of the drivers
+(found at the bottom of the page).
+
+Mirrors for this site are can be found at:
+
+ http://glide.mirrors.sonic.net
+ http://glide.samiam.org
+
+Please follow the installation instructions presented there for adding the
+correct drivers for your 3D-acceleration card. Note that there are two ways
+to install the 3DFX drivers-one with support for a /dev/3dfx device that
+does not require root permissions in order to use the card. If you choose to
+not use the /dev/3dfx method, you will have to run Q3Test as the root user.
+
+NOTE: Linux Q3Test will try to load "libGL.so" before using 3Dfx drivers.
+This can cause problems if you have a software or third party OpenGL driver
+installed. You can specifically target the included 3Dfx based Mesa driver
+by using the following command line:
+
+ ./linuxquake3 +set r_glDriver libMesaVoodooGL.so.3.1
+
+For 3DFX users, you may disable the vertical sync refresh. This can improve
+proformance at the cost of some visual tearing of the image. Entering the
+following command into your shell before running Q3Test will turn off
+the vertical sync:
+
+ export FX_GLIDE_SWAPINTERVAL=0
+
+Then run Linux Q3Test from the same command line normally.
+
++-----------+
+| X11 Setup |
++-----------+
+
+Linux Q3Test requires X11 to run. There is no console-based version as in
+previous id products such as GLQuake and Quake2. XFree86 version 3.2 or
+later is required.
+
+There are two ways that mouse input is handled under XFree86:
+
+ o By default, Q3Test will attempt to use DGA mouse handling. DGA
+ support features direct reading of the mouse motion and provides
+ more accurate control while playing the game. By default this
+ support is enabled, but can be disabled by adding "+set
+ in_dgamouse 0" to the command line at startup.
+ o The non-DGA method of mouse input uses pointer grabbing and warps
+ the pointer to the middle of the window on each mouse update. On
+ systems with a slow frame rate and a lot of mouse user input, the
+ motion can get "clipped" to the window boundaries. This method of
+ input is more compatible however.
+
+Q3Test uses the XFree86 VidModeExtension facilities if available to provide
+fullscreen play. This does not apply to 3Dfx passthrough based cards, since
+the passthrough cable takes over the video display upon activation anyway.
+
+When configuring your X11 server, make sure that you include lower
+resolution modes such as 640x480 and 800x600. Q3Test will auto-switch to
+these modes using the VidModeExtension if you select fullscreen from the
+graphics options menu. If the lower resolution modes are not listed in the
+XFree86 configuration file, Q3Test will be unable to switch to the desired
+resolution for fullscreen play.
+
++-------------------------+
+| Sound Support and Setup |
++-------------------------+
+
+Q3Test uses the /dev/dsp sound device for sound support under Linux. This is
+the default device provided by the sound drivers included with the Linux
+kernel. Please note that at the time of this writing, PCI based sound cards
+such as the SoundBlaster Live and Diamond Monster MX series were not
+supported. They may be supported in the future. Check
+http://www.opensound.com/ for support in the future.
+
+If you do not run Q3Test as root (recommended), you may need to make sure
+the permissions for the /dev/dsp device are read and write by the user
+running Q3Test. The cheap and easy way to make this happen is to do "chmod
+o+rw /dev/dsp" as root. For the more security conscious, a special sound
+group could be created and Q3Test could be made setgid to the sound group to
+access the device.
+
+Q3Test uses mmap() to map the sound buffers on /dev/dsp directly in order to
+provide responsive sound needs. Sound cards must be able to support this
+feature in order to work. SoundBlaster 16, AWE32 and AWE64 cards are known
+to work.
+
++----------------------+
+| WHAT'S IN THIS TEST? |
++----------------------+
+
+We have included three maps with this test version of Quake3: Arena. They
+are q3test1, q3test2, and q3tourney.
+
+q3test1 for 4-8 players on Internet or LAN play Best with 6 players
+q3test2 for 3-8 players Primarily for LAN play Best with 3 players
+q3tourney is the tournament map
+
+===================
+== Section 2. ==
+== BUG REPORTING ==
+===================
+
+What we would like from our testers are emails with descriptions of
+problems that are encountered, particularly those involving video or
+network bugs. Do NOT send reports to individual id employees. All bug
+reports should go to the following e-mail address:
+
+q3feedback@idsoftware.com
+
+In your subject line, please describe the system the game is being played
+on (Mac, Linux, Win32) and the type of problem you are reporting: video,
+network, sound or game. Example Subject Line: "Mac/video problem" or
+"Linux/network connection problem."
+
+In the body of your letter (no attached files please), briefly list and
+describe the problems. Detailed descriptions of problems are good, but
+remember that brevity is best. Please do NOT send screen shots unless
+they are the ONLY way to show a problem.
+
+While we realize that you may have comments and suggestions regarding
+specific game play features, please refrain from submitting such along
+with bug reports. Comments on game play can be made on the official
+Quake 3: Arena message board at:
+
+http://www.quake3arena.com
+
+========================
+== Section 3. ==
+== INSTALLING Q3TEST ==
+========================
+
+After you have installed the needed hardware drivers for your 3D
+acceleration and sound hardware, you need to install the Q3Test package.
+
+Q3Test is available as a Red Hat RPM file or as a tar file. To install the
+RPM file, enter the following when logged in as the root user:
+
+rpm -Uvh q3test-1.00-glibc-1.i386.rpm
+
+The rpm installs the game and data files in /usr/local/games/q3test.
+
+[ Note: If you have not installed (or do not wish to install) the Linux
+glide drivers, you may get an error message about not having libglide2x.so
+installed. This can be ignored and installation can be forced by adding
+the --nodeps option to the rpm command line as follows:
+rpm --nodeps -Uvh q3test-1.00-glibc-1.i386.rpm ]
+
+If you are going to install the tar version, make a directory for
+installation, untar the file as follows:
+
+tar xzvf q3test-1.00-glibc-i386.tar.gz
+
+This will untar the files into a q3test subdirectory under the current
+directory.
+
+====================
+== Section 4. ==
+== RUNNING Q3TEST ==
+====================
+
+It is recommended that you do not run Q3Test using the root account or with
+root privileges. There are two exceptions to this:
+
+ o If you are using a 3Dfx based accelerator card and do not install
+ the /dev/3dfx configuration option. (You will have to run as root
+ in order to access the card).
+ o If you do not have access to the /dev/dsp device and do not wish
+ to change the mode of the device so that non-root accounts can
+ access it.
+
+You will have to be running under X11 or have the DISPLAY variable pointed
+to a OpenGL glX capable X Server.
+
+By default, Q3Test tries to find the following OpenGL libraries in this
+order:
+
+ o libGL.so.1
+ o libMesaVoodooGL.so.3.1
+
+You can override the library name by entering, "+set r_glDriver <libname>"
+on the command line. This may be needed if you are using a non-standard set
+up and have a different name for the OpenGL shared library.
+
+If everything proceeds successfully, you should have a Q3Test window on your
+desktop with a menu displayed (3DFX owners will get a full screen view). If
+you want to use full screen, go to the System Configuration, Graphics
+Options, Fullscreen, change the value to Yes, and hit enter to apply it.
+
+If you intend to connect to the Internet to play Q3Test, make certain
+that your net connection is open and working first.
+
+======================
+== Section 5. ==
+== KNOWN ISSUES ==
+======================
+
+Q. My mouse is erratic. My player is constantly looking up or down and
+I can't seem to move him.
+
+A. This happens with two issues. Some versions of XFree86 have a bug in
+DGA mouse handling that confuses Q3Test. You can try turning off DGA mouse
+support by entering "+set in_dgamouse 0" on the command line when you
+start Q3Test. This problem can also occur after a restart of the video
+system (such as after a graphic setting change). This is because some
+sort of event occurred to jam up the mouse. Restarting the video system
+another time and not moving the mouse during the restart should clear it.
+
+
+Q. When I do a video restart (for example, selecting Apply in the Graphics
+Options menu), Q3Test exits with a Sig11.
+
+A. This is a driver related problem.
+On 3DFX hardware that is using the /dev/3dfx device and you are NOT running
+as root, it seems that the restart doesn't clear some sort of permission.
+Running Q3Test as root will fix this problem.
+
+
+Q. Performance is so slow I can't play! The frame rate looks like one
+frame per second!
+
+A. Most of the current drivers for Linux do not support hardware
+acceleration at any bit depth other than 15 or 16 bit color. Make sure
+that you specify 16 bit color when you run your X11 Server, such as:
+
+ startx -- -bpp 16
+
+
+Q. I have a Voodoo3. When I start the game, I send up at a low resolution
+version of my desktop.
+
+A. The current Voodoo3 X11 Servers have problems with DGA Mouse support.
+This is a known driver issue and will be fixed in newer revisions of the
+Voodoo3 X11 Server. Run the game with the following to disable it:
+ ./linuxquake3 +set in_dgamouse 0
+
+
+Q. I'm running Gnome with Englightenment and sound doesn't work.
+
+A. If you are using the Enlightenment Sound Daemon (esd), it will not release
+the sound hardware for Quake3 Test's usage. You can disable it before running
+the test as follows:
+ esdctl off # turn it off
+ start q3test the normal way
+ esdctl on # turn it back on
+
+
+Q. I try to run Q3Test, but sound doesn't work. The console says "Can't
+open /dev/dsp. /dev/dsp: Can't allocate memory."
+
+A. This can happen if the vmware kernel modules are loaded. You can unload
+them by typing when logged as root:
+ /etc/rc.d/init.d/vmware stop
+Assuming you are using a RedHat system.
+
+
+Q. I was playing Q3Test and suddenly it quit and reported a signal error.
+
+A. This is probably a bug. Please report it.
+
diff --git a/code/unix/README.Q3Test b/code/unix/README.Q3Test
new file mode 100755
index 0000000..eab97e8
--- /dev/null
+++ b/code/unix/README.Q3Test
@@ -0,0 +1,306 @@
++----------------------------------------+
+| Q3Test |
+| The Official Quake 3: Arena TEST |
+| Operating Instructions Readme |
+| Revision 2.1 |
+| Released Friday July 16th, 1999 |
+| Copyright (C) 1999 id Software, inc. |
++----------------------------------------+
+| Documentation: Paul Jaquays |
++----------------------------------------+
+
+This game is not yet rated by the ESRB. It contains animated
+violence and blood. A rating for mature audiences 17+ is
+anticipated.
+
+1. Introduction
+2. Operating Instructions
+3. Status Readouts
+4. Player Actions
+5. The Arena Environment
+6. The Game Console
+7. Problem Icons
+
+==================
+== Section 1. ==
+== INTRODUCTION ==
+==================
+
+ These instructions for game play should work with the versions of the game that are targeted at different platforms.
+
+ Before proceeding onward, click on the EULA_Readme file icon. This is the End User License Agreement. It's the agreement between you and id regarding this test. Read it. Understand that the acts of loading or running the software, or placing it on your computer hard drive, RAM, or other storage, you are agreeing to be bound by the terms of the End User License Agreement. So it's best to know what you are agreeing to.
+
+ If you agree to the conditions of the End User License Agreement, install the game on your computer's hard drive.
+ If you are using the Win32 version, open the readme file included for GLSetup. This application will load the most recent versions of the GL drivers needed to play Quake3: Arena.
+
+ The platform-specific instructions for the test are included in a separate Readme file. Refer to the appropriate Readme for your hardware.
+
++----------------+
+| THIS IS A TEST |
++----------------+
+ We want to set the record straight from the outset. This is neither a demo nor an ALPHA version of the final game and should not be expected to behave as such. Q3Test is a test of Quake3: Arena's network technology and graphics compatibility. While many of the game features that you will see in this test are in a highly polished state, do not confuse this with being anything other than what it is intended to be: a test. By running this test openly on the Internet and on a multitude of local networks, we hope to expose problems that could not otherwise be discovered on the limited number of in-house systems and computers we have available. That's why we need your assistance for this test.
+
+ Furthermore, it is anticipated that there may be ongoing revisions and additions made to the test that will make it incompatible with earlier versions.
+
+============================
+== Section 2. ==
+== OPERATING INSTRUCTIONS ==
+============================
+
+ When you start Q3Test, the game does not automatically load a map. Instead it displays the Main Menu screen. At this point (or at any time during the game) you may adjust and customize the video and audio settings on your computer, change details regarding your game character, and if desired, change the default controls to ones more to your liking.
+
+At any time during the game, press the Escape key to bring up the Main menu and superimpose it over the game screen. Use the up and down arrow keys to navigate the menu, and press the Enter key to select a function. Press Escape to return to the main level menu (and set any selected choices). Each Main menu function brings up a list of secondary menu items. If you have problems using the secondary menu selections, please refer to the message banner at the bottom of the screen for technical details.
+
+If a menu selection appears in gray, you cannot select or modify it. Choosing some options in the CONTROL CUSTOMIZATION and GRAPHICS OPTIONS menus will disallow other options. This is because they are either unnecessary (as is the case with MOUSE LOOK if you select the FREE LOOK option), or are not supported (as is the case with COLOR DEPTH if you chose the VOODOO OPENGL).
+
++-----------+
+| MAIN MENU |
++-----------+
+The first three Main Menu options, SEARCH FOR LAN GAME, CONNECT TO NETWORK GAME and START NETWORK GAME are only available if the player is not currently connected to an ongoing game.
+
+SEARCH FOR LAN GAME:
+If your computer is on a Local Area Network (LAN), this will show any currently active Q3Test servers on the network, along with the number of players currently in the game and the maximum number of players that can be in that game.
+
+Example: idserver q3test1 1/8
+
+This indicates that the server on the LAN called "idserver" is currently playing the map "q3test1" and has one player in it out of the possible 8 that can be in the game at one time.
+
+Use the up/down arrow keys to select a server and hit ENTER to command your computer to connect to that game.
+
+CONNECT TO NETWORK GAME:
+ SERVER: If your computer is connected to the Internet, you can enter the IP address of a server and hit ENTER to attempt to make a connection to that server. For server id's Q3Test server listings, go to http://www.quake3arena.com or check with your favorite 3D gaming web sites for other servers.
+ RATE: Set the type of network connection you are using. Choices are: "<=28.8K", "33.6K", "56K", "ISDN", and "LAN/cable/xDSL".
+
+START NETWORK GAME:
+To start a network game, whether on a LAN or over the Internet, select this option. The secondary menu screen has the following options:
+
+START: Launches the game using the parameters currently set on this menu.
+MAP: This gives access to the list of maps located in the Quake3/Baseq3/Maps directory/folder. For the purposes of this test, there are only two maps. Use the left and right arrow keys to toggle between the two maps.
+DEDICATED: This is either "no", "dedicated LAN," or "dedicated Internet."
+A dedicated server (set to YES) runs the game, but does not display a graphic version of the game on the screen of the computer running the game. The default value for this is no. This means that you are running the server and participating as a player at the same time.
+TIME LIMIT: entering a non-zero number here sets a time limit (in minutes) for the game duration. If the game has not ended because a frag limit has been set, then it will end when time runs out.
+FRAG LIMIT: Entering a non-zero number here sets the number of "frags" or "kills" needed to win the game. If the game has not ended because a time limit has been set, then it will end when one player in the game reaches this number of frags.
+MAX PLAYERS: This is the total number of players that may be in the game at any one time.
+HOSTNAME: This is the name of the server. In the LAN example above, the hostname is "idserver."
+
+CUSTOMIZE PLAYER:
+Allows the player to change information about his in-game persona.
+
+NAME: This is the player's name as it will appear on-screen during the game. It may be up to 33 characters long.
+
+SKIN: Visor is the only model in the test. Two skins (red and blue) are available for the model. Use the left and right arrow keys to select one.
+
+EFFECTS COLOR: You can choose an identifying color for your rail gun projectile trails. Use the left and right arrow keys to select a color.
+
+HANDICAP: Better players can choose to handicap themselves to balance a match against players of less skill by limiting the maximum amount of health and armor that they can have during the game. Use the left and right arrows to select a value from 25 to 100 points. Certain items and power ups will temporarily raise this value, but will then count down to the preset value. The default is 100.
+
+CUSTOMIZE CONTROLS:
+Allows the player to change the assignment of game controls to other keyboard and mouse buttons. When you visit this menu for the first time, the default assignments for all controls are shown. To change an assignment, use the up/down arrow keys to select a game function. Press ENTER to activate the change process. Press the keyboard key or mouse input that you want to use for that function. Up to two keys/buttons may be assigned to each game function in this manner by repeating the process. If you inadvertently use a key assigned to another function, three question marks ("???") appear next to the function that previously used that key.
+
+ALWAYS RUN: setting this to YES causes your player to move faster, but make more noise as he moves. The RUN/WALK key allows the player to switch between fast/noisy and slow/silent movement modes.
+FREELOOK: If YES, then you may look freely in all directions as if you were moving only your head by moving the mouse around. If this is Set to NO you must bind a key to mouse look.
+INVERT MOUSE: Selecting YES inverts the up and down view movement directions.
+MOUSE SPEED: This is set on the menu with a slide control. The faster your mouse speed, the greater the on-screen response to small movements of the mouse. Mouse speed can also be adjusted from the console. Bring the console down and type in "sensitivity". The game will respond by printing "sensitivity" is: "__" (the blank indicating the number value currently assigned to mouse speed) default is "5". Use the up arrow key to display sensitivity on the console again, enter a different number value and hit enter. Higher numbers are more sensitive. Lower numbers are less sensitive.
+
+SYSTEM CONFIGURATION:
+EFFECTS VOLUME: Use the slide control to adjust game sound playback volume to your liking.
+SOUND QUALITY: Select either Low (16 bit sound running at 11 megahertz) or High (32 bit sound running at 22 megahertz).
+BRIGHTNESS: First, try running the game with the brightness set to 1.0 (the default level used by the designer when making the map). If the map is too dark to see in dimly lighted areas or too light in sunny areas, use this slide control to compensate for differences in the way some monitors and video cards draw the game world. Remember that the arenas are supposed to be dark and foreboding places.
+
+GRAPHICS OPTIONS:
+This drops you down into another menu layer. The controls here are designed to help you optimize graphics and or/enhance the game's playability on your system. When you are satisfied with your selections, you must move the cursor to the bottom of the screen to the APPLY option to activate them. Pressing Escape to return to the SYSTEM CONFIGURATION MENU does not activate changes in the graphics settings. Changing anything on this menu will cause the game to restart the video.
+
+GRAPHICS MODE: This is a global setting for the display of the game's screen graphics. The settings are HIGH QUALITY, NORMAL, FAST, FASTEST, and CUSTOM. Selecting one of these choices affects most of the other settings on this page. If you make any adjustments to other settings on this page, the graphics mode changes to CUSTOM.
+DRIVER: Consult the platform specific readme for the drivers that will work with your platform.
+VIDEO MODE: This is the screen resolution size. Smaller resolutions can improve performance speed on slower machines.
+COLOR DEPTH: Determines whether you are using 16-bit textures or 32-bit textures. Default means you run with the color depth currently set for your computer's desktop.
+FULLSCREEN: A YES selection allows you to choose to run with the test filling the entire screen. A NO selection will run the game in a window. If run in a window, COLOR DEPTH is disabled because you are limited to whatever has been selected for the desktop.
+LIGHTING: Allows you to chose either a LIGHTMAP or VERTEX lighting. Using the LIGHTMAP is recommended for best appearance. VERTEX lighting may improve game performance, but at a cost to appearance.
+GEOMETRIC DETAIL: Lets you adjust the number subdivisions used to define curves in the arena on both world geometry and items, weapons and characters. HIGH is the normal setting. LOW greatly reduces curve complexity.
+SCREEN SIZE: The slide control changes the amount of screen area devoted to display of the game. Reducing screen size can improve performance.
+TEXTURE DETAIL: This sliding control allows you to adjust the level of detail displayed. Unless you have an extremely fast computer, the normal setting gives the best balance of detail vs. game speed.
+TEXTURE QUALITY: This determines whether you wish to use 32-bit textures, 16-bit textures, or compressed textures. Many of the game's special effects will appear at their best only when defined by 32-bit textures.
+OPENGL EXTENSIONS: Allows you to enable or disable OPENGL extensions.
+DRIVER INFORMATION: Brings up a display of information regarding the drivers currently operating in your machine. It is not interactive. Hit ESC to return to the previous menu.
+
+
+PREFERENCES: Hitting ENTER here will drop you into another menu that allows you to adjust other game features.
+CROSSHAIR: choose one of four options, none, cross, dot, or angle.
+MARKS ON WALLS: Enable or Disable weapons scars and blood smears. Disabling this feature may improve performance.
+DYNAMIC LIGHTS: Enable or Disable lights that move through the world (such as light generated by weapon projectile trails). Disabling this feature may improve performance.
+LIGHT FLARES: Enable or DISABLE glowing light halos around lights. Disabling this feature may improve performance.
+IDENTIFY TARGET: Enable or Disable the ability to see the name of your foe when you put your crosshair on him (does not function if target is in fog).
+SKY: Set the sky for either HIGH SPEED or HIGH QUALITY.
+
+RESET DEFAULTS: Restore all game settings and controls to their default value by reloading the default configuration.
+
+APPLY: You must select this and hit ENTER to lock in your choices. Doing so will restart the game video.
+
+SCREENSHOT:
+Makes a targa file screen shot of what is currently in view on the screen. Look for it in the Screenshot folder within your Quake 3/Baseq3/screenshots folder.
+
+LEAVE ARENA:
+This disconnects you from the arena you are currently playing, but does not quit you out of the game. From here you can connect to other servers or start your own. You can also leave an arena by typing "disconnect" on the command line of the console.
+
+QUIT GAME:
+Returns you to the desktop.
+
+
+======================
+== Section 6. ==
+== STATUS READOUTS ==
+======================
+
+ Your player status is shown as numbers and icons floating in your view at the bottom of the screen. There is an indicator for your ammo (far left) including the type of ammo currently in use, current health (left-center), your character portrait (center), armor (center-right), and Frag Count (far right). The digital timer in the upper right shows the amount of time you have been in this arena. When you have a one-use item in your inventory (such as the personal teleporter), its icon will appear on the right side of your display. When you are fragged, the character portrait and name of the foe who killed you appears in the upper right corner of the display. When you change weapons, icons representing the ammo of the weapons in your inventory will appear above your character portrait. If you have a weapon, but no ammunition for it, a negation symbol will be superimposed over the ammo icon.
+
++----------------------------+
+| INTERPRETING THE LAGOMETER |
++----------------------------+
+ The Lagometer is the graph in the lower right corner of the status display. It is a test function and not a part of regular game play. The upper graph (blue/yellow) slides one pixel for every rendered frame. Blue lines below the baseline mean that the frame is interpolating between two valid snapshots. Yellow lines above the baseline mean the frame is extrapolating beyond the latest valid time. The length of the line is proportional to the time.
+The lower graph (green/yellow/red) slides one pixel for every received snapshot. By default, snapshots come 20 times a second, so if you are running >20 fps, the top graph will move faster, and vice versa. A red bar means the snapshot was dropped by the network. Green and yellow bars are properly received snapshots, with the height of the bar proportional to the ping. A yellow bar indicates that the previous snapshot was intentionally suppressed to stay under the rate limit.
+The upper graph indicates the consistency of your connection. Ideally, you should always have blue bars of only a pixel or two in height. If you are commonly getting big triangles of yellow on the graph, your connection is inconsistent.
+ In a heavy firefight, it is normal for modem players to see yellow bars in the bottom graph, which should return to green when the action quiets down. If you are getting several red bars visible, you may want to look for a server that drops less packets.
+ There are a few tuning variables for people trying to optimize their connection: The most important one is "rate", which is what the connection speed option in the menu sets. We are fairly conservative with the values we set for the given modem speeds: 2500 for 28.8, 3000 for 33, and 3500 for 56k. You may actually be connecting faster than that, and modem compression may be buying you something, so you might get a better play experience by increasing the values slightly. If you connect at 50000 bps, try a rate of 5000, etc. Quake 3 errs on the conservative side, because too low of a rate will only make the movement of other things in the world choppy, while too high of a rate can cause huge amounts of lag.
+ Note that the optimal rate will be somewhat lower than a rate for QW or Q2, because Quake 3 now includes the UDP packet header length in the bandwidth estimate. You can ask for a different number of snapshots by changing the "snaps" variable, but there isn't a lot of benefit to that. Dedicated servers run at 40hz, so stick to divisors of that: 40, 20 (default), 10. A "snaps" of 40 will usually just cause you to hit your rate limit a lot faster. It may be useful for tuning rate, if nothing else.
+ You can adjust the local timing point with "cg_timenudge ", which effectively adds local lag to try to make sure you interpolate instead of extrapolate. If you really want to play on a server that is dropping a ton of packets, a timenudge of 100 or so might make the game smoother.
+
+=====================
+== Section 7. ==
+== PLAYER ACTIONS ==
+=====================
+
+ The descriptions below are based on the preset default configurations for the game controls. If you have played first person shooting games previously (such as Quake or Quake 2), you may want to reconfigure the controls to match your preferences.
+
++----------+
+| ACTIONS |
++----------+
+WALKING: Use the four arrow keys or the mouse to walk around. To walk steadily forward, hold down the "forward" key (the up arrow or center button on a 3-button mouse). Turn left or right with the left or right arrow keys, or by sliding your mouse in the desired direction.
+
+RUNNING: To run, simply hold down the left Shift key to increase your
+Speed (unless you have selected "Always Run", in which case it reverts you to a walk. If you prefer to always run during the game, open the Main menu, then the Customize Controls menu, and select Always Run.
+
+SHOOTING: Tap the Ctrl key or the left mouse button to fire. Hold the
+fire key down to keep firing.
+
+LOOKING UP AND DOWN: The letters A and Z allow you to look up and down,
+respectively. Start walking forward and your line of vision will
+automatically readjust to the center position. You may also choose to
+select Free Look under the Controls menu which will allow you to move the
+mouse around as if it was your head movement.
+
+SWIMMING: While underwater, aim yourself in the direction you wish to go
+and press the forward key to go in that direction. Unfortunately, as in
+real life, you may lose your bearings while underwater. Use the jump key,
+(default is the Space Bar), to kick straight up towards the surface. Once on
+the surface, tread water by holding down the jump key. To get out of the
+drink, swim toward the shore and you can either jump up onto the land or
+walk straight out if it is shallow enough. There is always a way out of
+water, but you may have to submerge even deeper in order to find it.
+
+CROUCHING: Press and hold down the C key to duck and move forward to "duck walk." When you release the C key, you will return to an upright position. It is also possible to avoid rockets in this manner.
+
+JUMPING: Tap the space bar to jump. You jump farther if you're moving
+forward at the time.
+
+STRAFING: Hold down the ALT key and the left or right arrow key and you will side-step in that particular direction. This is perfect for avoiding incoming weapon attacks.
+
+PICKING UP OBJECTS: To pick up items, weapons, and power-ups, simply walk
+over them. The weapon cross-hair will pulse larger briefly to indicate you have acquired a new weapon or supply. If you are unable to pick something up, it means you already have the maximum amount possible for that object.
+
+TALKING: To communicate with other players, press the T key
+and type in your message. Press Enter when finished and your words will
+appear at the top of the screen. You can also enter communications on the command line of the console.
+
+ATTACKING: Point your weapon at an opponent and press the fire button. Default fire keys are the left mouse and the control key). If a weapon runs out of ammo, it switches to the next available weapon with ammo the next time you fire it. If you have no ammo at all, you fight with the gauntlet.
+
++--------+
+| DYING: |
++--------+
+ When you die, the weapon you were using (except for the gauntlet and the machine gun), and any active power ups drop where you died. Armor, ammo and one-use items like the teleporter are lost. To respawn into the game, press the fire key. When you re-enter the game you are once again armed with only your starting equipment.
+
+===========================
+== Section 8. ==
+== THE ARENA ENVIRONMENT ==
+===========================
+
++-----------------------+
+| RAPID TRANSIT DEVICES |
++-----------------------+
+TELEPORTERS: These devices instantaneously move you to a pre-selected destination.
+
+PORTALS: These are devices that generate holes in space through which you can see your ultimate destination and move to it like walking through a doorway.
+
+JUMP PADS: Jump pads throw you upwards to land on higher portions of the arena. You have some "air" control over your final landing point.
+
+ACCELERATION PADS: Acceleration pads turn you into a human cannon ball, launching you to distant locations in the arena. As with the jump pads, you have some "air" control over your final landing point.
+
++-----------------------+
+| ENVIRONMENTAL HAZARDS |
++-----------------------+
+LAVA: Walking on lava is not instantly fatal, but even with good armor protection, it can be a relatively quick (and noisy) doom.
+
+THE VOID: Falling off into the infinite blackness of the void is a near inescapable death.
+
++--------------+
+| TEST ARSENAL |
++--------------+
+MACHINE GUN: This is your default weapon. It's an instant hit attack and reasonably accurate even at long range. It uses bullets.
+
+GAUNTLET: This is either your weapon of last resort when you're out of ammo or a cool tool to humiliate other players. When it hits, it does damage and knocks a foe back. It does not require ammunition, but you have to be in direct contact with a foe and activating (firing) it to cause injury and knockback.
+
+SHOTGUN: This uses shells for ammunition. The spread of the shotgun blast makes it deadly in close combat, but not the best choice for long-range attacks.
+
+ROCKET LAUNCHER: The rocket launcher delivers heavy fire power to your target, but travels slower compared to other attacks. Be careful when you use this weapon in close combat.
+
+PLASMA GUN: Powered by Energy cells, this rapid-fire weapon delivers deadly gouts of glowing energy quickly and accurately.
+
+RAIL GUN: The Rail Gun fires depleted uranium slugs at super high velocities. It hits instantly and can pass through the bodies of multiple opponents. But be warned, it recharges slowly and can be a liability in close combat. Take note of the distinctive trail of atmospheric disturbance
+caused by the projectile. The color of the rail gun's trail can be changed under the CUSTOMIZE PLAYER menu on the EFFECTS COLOR line.
+
+LIGHTNING GUN: Zap them!
+
++------------------+
+| COMBAT SUPPLIES: |
++------------------+
+Pick up these helpful items whenever possible. Some items take effect when
+picked up, others you must activate during play.
+
+AMMO: With the exception of the gauntlet, each weapon has a corresponding ammo type. The color of the ammo boxes corresponds with the color of the graphic weapon icons displayed briefly in your heads up display. Each ammo type has a maximum you can carry.
+
+ARMOR: There are three armor types: Green armor shards, yellow combat armor and red body armor. Collecting them increases your armor protection. The red armor and the green armor shards can increase your total armor protection above the maximum for short duration. The points "decay" back to the normal maximum value.
+
+HEALTH: There are three types of standard health kits: small (green), medium (yellow) and large (red) encased in crystalline shells. The green health can increase your total health value above the maximum. Like armor, it "decays" back to your maximum possible health value.
+
+QUAD DAMAGE: The quad temporarily multiplies all your weapon's strengths.
+
+MEGA HEALTH: This blue health power up provides a temporary but significant boost to your health, taking it above your maximum level. Like armor, it "decays" back to your maximum possible health value.
+
+PERSONAL TELEPORTER: Grab this single-use power up and use it to escape from dangerous situations. Activating it immediately teleports you to one of the arena's spawn spots.
+
+=======================
+== Section 9. ==
+== THE GAME CONSOLE ==
+=======================
+
+ The game console (or just the console) is a pull down window shade that shows recent game information, including the text of conversations and recently executed game commands. The bottom line of the console is the command line. While playing Q3test, you can enter into console mode by pressing the tilde (~) key and exit by pressing it again. The console is where you enter all the commands to change the game state.
+
+ For example, one console command is "map <name>" which will load a map
+and start it up. Starting up a map actually starts a local Server and a Client. For the purposes of this test, most of the console commands will be executed to optimize graphics. Pressing the up and down arrows in the console will change to the last command entered. You can backspace over the command and enter new values if you'd like. There is also a command-completion function. Type in the first few letters of a command. By pressing the TAB key, you can have the console automatically finish the name of the command for you.
+
+ When you are in console mode (or when you have selected to "talk" by typing in words to appear on the screen), a blue "voice balloon" will appear over your head. Common game courtesy is to NOT shoot players who are in this mode.
+
+=======================
+== Section 10. ==
+== PROBLEM ICONS ==
+=======================
+
+Q3Test has a Problem Icon that you may see on your screen from time to time. When you are disconnected from a Quake Server or the network packet flow is disturbed, you will see a Network Disconnect icon above the spot where your frag count is displayed. Other players will see it displayed over your character's head (similar to the way the talk balloon is displayed). If you are not immediately reconnected to the server, you will need to disconnect (leave arena) and attempt to reconnect with the server by either using the menu commands or by typing "connect" <server IP address> in the console command line.
+ NOTE: Courteous players will refrain from getting cheap points by fragging lagged out opponents.
+
+=======================
+== Section 11. ==
+== MESSAGE OF THE DAY ==
+=======================
+
+When Quake 3 Arena starts a map up, it sends the GL_RENDERER string to the Message Of The Day server at id. This responds back with a message of the day to the client. If you wish to switch this option off, set CL_MOTD to 0 (+set CL_MOTD 0 from the command line).
+
diff --git a/code/unix/build_setup.sh b/code/unix/build_setup.sh
new file mode 100755
index 0000000..c1a5f6d
--- /dev/null
+++ b/code/unix/build_setup.sh
@@ -0,0 +1,125 @@
+#!/bin/bash
+# Build various setups..
+
+# inputs:
+# directory with the common media
+Q3SETUPMEDIA=/home/timo/Id/Q3SetupMedia/quake3
+# directory with binaries tree
+Q3BINARIES=../install
+# version: $1
+VERSION=$1
+# temporary directory used to prepare the files
+# NOTE: this dir is erased before a new setup is built
+TMPDIR=setup.tmp
+
+# location of the setup dir (for graphical installer and makeself)
+SETUPDIR=setup
+
+# cp setup phase
+# we need to copy the symlinked files, and not the symlinks themselves
+# on antares this is forced with a cp -L
+# on spoutnik, -L is not recognized, and dereference is the default behaviour
+# we need a robust way of checking
+TESTFILE=/tmp/foo$$
+touch $TESTFILE
+# see if option is supported
+cp -L $TESTFILE $TESTFILE.cp 2>/dev/null
+if [ $? -eq 1 ]
+then
+ # option not supported, should be on by default
+ echo "cp doesn't have -L option"
+ unset CPOPT
+else
+ # option supported, use it
+ echo "cp supports -L option"
+ CPOPT="-L"
+fi
+rm $TESTFILE
+
+
+# graphical installer (based on Loki Software's Setup tool)
+build_installer ()
+{
+TMPDIR=setup.tmp
+
+rm -rf $TMPDIR
+mkdir $TMPDIR
+
+# copy base setup files
+cp $CPOPT -R $SETUPDIR/setup.sh $SETUPDIR/setup.data $TMPDIR
+
+# copy media files
+cp $CPOPT -R $Q3SETUPMEDIA/* $TMPDIR
+
+# remove CVS entries
+find $TMPDIR -name CVS | xargs rm -rf
+
+# copy binaries
+mkdir -p $TMPDIR/bin/x86
+# smp
+cp $CPOPT $Q3BINARIES/linuxquake3-smp $TMPDIR/bin/x86/quake3-smp.x86
+strip $TMPDIR/bin/x86/quake3-smp.x86
+brandelf -t Linux $TMPDIR/bin/x86/quake3-smp.x86
+# old school
+cp $CPOPT $Q3BINARIES/linuxquake3 $TMPDIR/bin/x86/quake3.x86
+strip $TMPDIR/bin/x86/quake3.x86
+brandelf -t Linux $TMPDIR/bin/x86/quake3.x86
+# ded
+cp $CPOPT $Q3BINARIES/linuxq3ded $TMPDIR/bin/x86/q3ded
+strip $TMPDIR/bin/x86/q3ded
+brandelf -t Linux $TMPDIR/bin/x86/q3ded
+
+# PB files
+mkdir -p $TMPDIR/pb/htm
+cp $CPOPT ../pb/linux/*.so $TMPDIR/pb
+cp $CPOPT ../pb/htm/*.htm $TMPDIR/pb/htm
+
+# Linux FAQ
+mkdir -p $TMPDIR/Docs/LinuxFAQ
+cp $CPOPT LinuxSupport/* $TMPDIR/Docs/LinuxFAQ
+
+# generated .qvm pk3 files
+mkdir -p $TMPDIR/baseq3
+mkdir -p $TMPDIR/missionpack
+# not needed now
+#cp $CPOPT $Q3BINARIES/baseq3/pak8.pk3 $TMPDIR/baseq3/
+#cp $CPOPT $Q3BINARIES/missionpack/pak3.pk3 $TMPDIR/missionpack/
+
+# menu shortcut to the game
+# FIXME current setup doesn't have a way to set symlinks on arbitrary things
+# so we use a dummy quake3 script (which will be overwritten by postinstall.sh)
+echo -e "#!/bin/sh\necho \"If you read this, then the setup script failed miserably.\nPlease report to ttimo@idsoftware.com\n\"" > $TMPDIR/bin/x86/quake3
+echo -e "#!/bin/sh\necho \"If you read this, then the setup script failed miserably.\nPlease report to ttimo@idsoftware.com\n\"" > $TMPDIR/bin/x86/quake3-smp
+# create the auto-extractible archive
+# first step: on FreeBSD we would default to Linux binaries .. use a symlink
+(
+cd $TMPDIR/setup.data/bin
+ln -s Linux FreeBSD
+ln -s Linux NetBSD
+ln -s Linux OpenBSD
+)
+# NOTE: we used to pass the $VERSION, but it doesn't seem very usefull
+./$SETUPDIR/makeself/makeself.sh $TMPDIR linuxq3apoint-$VERSION.x86.run "Quake III Arena Point Release $VERSION " ./setup.sh
+
+chmod a+rx linuxq3apoint-$VERSION.x86.run
+
+#rm -rf $TMPDIR
+}
+
+check_brandelf()
+{
+ # make sure brandelf is installed to avoid any problem when building the setups
+ BRAND=`which brandelf`;
+ if [ -n "$BRAND" ] && [ -x "$BRAND" ]
+ then
+ echo "brandelf is present: $BRAND"
+ else
+ echo "brandelf not found"
+ exit
+ fi
+}
+
+# safe checks
+check_brandelf
+
+build_installer
diff --git a/code/unix/cons b/code/unix/cons
new file mode 100755
index 0000000..450488e
--- /dev/null
+++ b/code/unix/cons
@@ -0,0 +1,6828 @@
+#!/usr/bin/env perl
+
+# NOTE: Cons intentionally does not use the "perl -w" option or
+# "use strict." Because Cons "configuration files" are actually
+# Perl scripts, enabling those restrictions here would force them
+# on every user's config files, wanted or not. Would users write
+# "better" Construct and Conscript files if we forced "use strict"
+# on them? Probably. But we want people to use Cons to get work
+# done, not force everyone to become a Perl guru to use it, so we
+# don't insist.
+#
+# That said, Cons' code is both "perl -w" and "use strict" clean.
+# Regression tests keep the code honest by checking for warnings
+# and "use strict" failures.
+
+use vars qw( $CVS_id $CVS_ver $ver_num $ver_rev $version );
+
+$CVS_id = 'Id';
+$CVS_ver = (split(/\s+/, $CVS_id))[2];
+
+$ver_num = "2.3";
+$ver_rev = ".1";
+
+$version = "This is Cons $ver_num$ver_rev ($CVS_id)\n";
+
+# Cons: A Software Construction Tool.
+# Copyright (c) 1996-2001 Free Software Foundation, Inc.
+#
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 2 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; see the file COPYING. If not, write to
+# the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+# Boston, MA 02111-1307, USA.
+
+require 5.003;
+# See the NOTE above about why Cons doesn't "use strict".
+use integer;
+use Cwd;
+use File::Copy;
+
+use vars qw( $_WIN32 $_a $_exe $_o $_so );
+
+#------------------------------------------------------------------
+# Determine if running on win32 platform - either Windows NT or 95
+#------------------------------------------------------------------
+
+use vars qw( $PATH_SEPARATOR $iswin32 $_WIN32 $usage $indent @targets );
+
+BEGIN {
+ use Config;
+
+ # if the version is 5.003, we can check $^O
+ if ($] < 5.003) {
+ eval("require Win32");
+ $_WIN32 = (!$@);
+ } else {
+ $_WIN32 = ($^O eq "MSWin32") ? 1 : 0;
+ }
+
+ # Fetch the PATH separator from Config;
+ # provide our old defaults in case it's not set.
+ $PATH_SEPARATOR = $Config{path_sep};
+ $PATH_SEPARATOR = $_WIN32 ? ';' : ':' if ! defined $PATH_SEPARATOR;
+
+ # Fetch file suffixes from Config,
+ # accomodating differences in the Config variables
+ # used by different Perl versions.
+ $_exe = $Config{_exe};
+ $_exe = $Config{exe_ext} if ! defined $_exe;
+ $_exe = $_WIN32 ? '.exe' : '' if ! defined $_exe;
+ $_o = $Config{_o};
+ $_o = $Config{obj_ext} if ! defined $_o;
+ $_o = $_WIN32 ? '.obj' : '.o' if ! defined $_o;
+ $_a = $Config{_a};
+ $_a = $Config{lib_ext} if ! defined $_a;
+ $_a = $_WIN32 ? '.lib' : '.a' if ! defined $_a;
+ $_so = ".$Config{so}";
+ $_so = $_WIN32 ? '.dll' : '.so' if ! defined $_so;
+}
+
+# Flush stdout each time.
+$| = 1;
+
+# Seed random number generator.
+srand(time . $$); # this works better than time ^ $$ in perlfunc manpage.
+
+$usage = q(
+Usage: cons <arguments> -- <construct-args>
+
+Arguments can be any of the following, in any order:
+
+ <targets> Build the specified targets. If <target> is a directory
+ recursively build everything within that directory.
+
+ +<pattern> Limit the cons scripts considered to just those that
+ match <pattern>. Multiple + arguments are accepted.
+
+ <name>=<val> Sets <name> to value <val> in the ARG hash passed to the
+ top-level Construct file.
+
+ -cc Show command that would have been executed, when
+ retrieving from cache. No indication that the file
+ has been retrieved is given; this is useful for
+ generating build logs that can be compared with
+ real build logs.
+
+ -cd Disable all caching. Do not retrieve from cache nor
+ flush to cache.
+
+ -cr Build dependencies in random order. This is useful when
+ building multiple similar trees with caching enabled.
+
+ -cs Synchronize existing build targets that are found to be
+ up-to-date with cache. This is useful if caching has
+ been disabled with -cc or just recently enabled with
+ UseCache.
+
+ -d Enable dependency debugging.
+
+ -f <file> Use the specified file instead of "Construct" (but first
+ change to containing directory of <file>).
+
+ -h Show a help message local to the current build if
+ one such is defined, and exit.
+
+ -k Keep going as far as possible after errors.
+
+ -o <file> Read override file <file>.
+
+ -p Show construction products in specified trees.
+ -pa Show construction products and associated actions.
+ -pw Show products and where they are defined.
+
+ -q Be quiet; multiple -q flags increase quietness level:
+ 1: quiet about Installing and Removing targets
+ 2: quiet about build commands, up-to-date targets
+
+ -r Remove construction products associated with <targets>
+
+ -R <repos> Search for files in <repos>. Multiple -R <repos>
+ directories are searched in the order specified.
+
+ -S <pkg> Use package sig::<pkg> to calculate file signatures.
+ Currently supported values are "md5" for MD5
+ signatures (the default) and "md5::debug" for MD5
+ signature debug information.
+
+ -t Traverse up the directory hierarchy looking for a
+ Construct file, if none exists in the current directory.
+ (Targets will be modified to be relative to the
+ Construct file.)
+
+ -v Show cons version and continue processing.
+ -V Show cons version and exit.
+
+ -wf <file> Write all filenames considered into <file>.
+
+ -x Show this message and exit.
+
+
+ Please report any suggestions through the cons-discuss@gnu.org mailing
+ list.
+
+ To subscribe, send mail to cons-discuss-request@gnu.org with body
+ 'subscribe'.
+
+ If you find a bug, please report it through the bug-cons@gnu.org
+ mailing list.
+
+ Information about CONS can be obtained from the official cons web site
+ http://www.dsmit.com/cons/ or its mirrors (listed there).
+
+ The cons maintainers can be contacted by email at cons-maintainers@gnu.org
+
+ User documentation of cons is contained in cons and can be obtained
+ by doing 'perldoc /path/to/cons'.
+
+);
+
+# Simplify program name, if it is a path.
+{
+ my ($vol, $dir, $file) = File::Spec->splitpath(File::Spec->canonpath($0));
+ $0 = $file;
+}
+
+# Default parameters.
+$param::topfile = 'Construct'; # Top-level construction file.
+$param::install = 1; # Show installations
+$param::build = 1; # Build targets
+### $param::show = 1; # Show building of targets.
+$param::sigpro = 'md5'; # Signature protocol.
+$param::depfile = ''; # Write all deps out to this file
+$param::salt = ''; # Salt derived file signatures with this.
+$param::sourcesig = ['*' => 'content'];# Source file signature calculation
+$param::rep_sig_times_ok = 1; # Repository .consign times are in sync
+ # w/files.
+$param::conscript_chdir = 0; # Change dir to Conscript directory
+$param::quiet = 0; # should we show the command being executed.
+
+@param::defaults = ();
+
+#
+$indent = '';
+
+# Display a command while executing or otherwise. This
+# should be called by command builder action methods.
+sub showcom {
+ print($indent . $_[0] . "\n") if ($param::quiet < 2);
+}
+
+# Default environment.
+# This contains only the completely platform-independent information
+# we can figure out. Platform-specific information (UNIX, Win32)
+# gets added below.
+@param::base = (
+ 'SIGNATURE' => [ '*' => 'build' ],
+ 'SUFEXE' => $_exe, # '' on UNIX systems
+ 'SUFLIB' => $_a, # '.a' on UNIX systems
+ 'SUFLIBS' => "$_so:$_a", # '.so:.a' on UNIX
+ 'SUFOBJ' => $_o, # '.o' on UNIX systems
+ 'SUFMAP' => {
+ '.c' => 'build::command::cc',
+ '.s' => 'build::command::cc',
+ '.S' => 'build::command::cc',
+ '.C' => 'build::command::cxx',
+ '.cc' => 'build::command::cxx',
+ '.cxx'=> 'build::command::cxx',
+ '.cpp'=> 'build::command::cxx',
+ '.c++'=> 'build::command::cxx',
+ '.C++'=> 'build::command::cxx',
+ },
+ 'PERL' => $^X,
+);
+
+%param::rulesets =
+ (
+ # Defaults for Win32.
+ # Defined for VC++ 6.0 by Greg Spencer <greg_spencer@acm.org>
+ # Your mileage may vary.
+ 'msvc' => [
+ 'CC' => 'cl',
+ 'CFLAGS' => '/nologo',
+ 'CCCOM' => '%CC %CFLAGS %_IFLAGS /c %< /Fo%>',
+ 'CXX' => '%CC',
+ 'CXXFLAGS' => '%CFLAGS',
+ 'CXXCOM' => '%CXX %CXXFLAGS %_IFLAGS /c %< /Fo%>',
+ 'INCDIRPREFIX' => '/I',
+ 'INCDIRSUFFIX' => '',
+ 'LINK' => 'link',
+ 'LINKCOM' => '%LINK %LDFLAGS /out:%> %< %_LDIRS %LIBS',
+ 'LINKMODULECOM' => '%LD /r /o %> %<',
+ 'LIBDIRPREFIX' => '/LIBPATH:',
+ 'LIBDIRSUFFIX' => '',
+ 'AR' => 'lib',
+ 'ARFLAGS' => '/nologo ',
+ 'ARCOM' => "%AR %ARFLAGS /out:%> %<",
+ 'RANLIB' => '',
+ 'LD' => 'link',
+ 'LDFLAGS' => '/nologo ',
+ 'PREFLIB' => '',
+ ],
+ # Defaults for a typical (?) UNIX platform.
+ # Your mileage may vary.
+ 'unix' => [
+ 'CC' => 'cc',
+ 'CFLAGS' => '',
+ 'CCCOM' => '%CC %CFLAGS %_IFLAGS -c %< -o %>',
+ 'CXX' => '%CC',
+ 'CXXFLAGS' => '%CFLAGS',
+ 'CXXCOM' => '%CXX %CXXFLAGS %_IFLAGS -c %< -o %>',
+ 'INCDIRPREFIX' => '-I',
+ 'INCDIRSUFFIX' => '',
+ 'LINK' => '%CXX',
+ 'LINKCOM' => '%LINK %LDFLAGS -o %> %< %_LDIRS %LIBS',
+ 'LINKMODULECOM' => '%LD -r -o %> %<',
+ 'LIBDIRPREFIX' => '-L',
+ 'LIBDIRSUFFIX' => '',
+ 'AR' => 'ar',
+ 'ARFLAGS' => 'r', # rs?
+ 'ARCOM' => ['%AR %ARFLAGS %> %<', '%RANLIB %>'],
+ 'RANLIB' => 'ranlib',
+ 'AS' => 'as',
+ 'ASFLAGS' => '',
+ 'ASCOM' => '%AS %ASFLAGS %< -o %>',
+ 'LD' => 'ld',
+ 'LDFLAGS' => '',
+ 'PREFLIB' => 'lib',
+ 'ENV' => { 'PATH' => '/bin:/usr/bin' },
+ ],
+ );
+
+# Set the rules based on the platform.
+script::DefaultRules(script::RuleSet($_WIN32 ? 'msvc' : 'unix'));
+
+# Handle command line arguments.
+while (@ARGV) {
+ $_ = shift @ARGV;
+ last if /^--$/; # Argument passing to Construct.
+ &option, next if s/^-//;
+ push (@param::include, $_), next if s/^\+//;
+ &equate, next if /=/;
+ push (@targets, $_), next;
+}
+
+sub option {
+ my %opt = (
+ 'cc' => sub { $param::cachecom = 1; },
+ 'cd' => sub { $param::cachedisable = 1; },
+ 'cr' => sub { $param::random = 1; },
+ 'cs' => sub { $param::cachesync = 1; },
+ 'd' => sub { $param::depends = 1; },
+ 'h' => sub { $param::localhelp = 1; },
+ 'k' => sub { $param::kflag = 1; },
+ 'p' => sub { $param::pflag = 1;
+ $param::build = 0; },
+ 'pa' => sub { $param::pflag = 1;
+ $param::aflag = 1;
+ $indent = "... ";
+ $param::build = 0; },
+ 'pw' => sub { $param::pflag = 1;
+ $param::wflag = 1;
+ $param::build = 0; },
+ 'q' => sub { $param::quiet++; },
+ 'r' => sub { $param::rflag = 1;
+ $param::build = 0; },
+ 't' => sub { $param::traverse = 1; },
+ 'v' => sub { print($version); },
+ 'V' => sub { print($version), exit(0); },
+ 'x' => sub { print($usage), exit 0; },
+ );
+
+ my %opt_arg = (
+ 'f' => sub { $param::topfile = $_[0]; },
+ 'o' => sub { $param::overfile = $_[0]; },
+ 'R' => sub { script::Repository($_[0]); },
+ 'S' => sub { $param::sigpro = $_[0]; },
+ 'wf' => sub { $param::depfile = $_[0]; },
+ );
+
+ if (defined $opt{$_}) {
+ &{$opt{$_}}();
+ return;
+ }
+ while ($_) {
+ $_ =~ m/(.)(.*)/;
+ if (defined $opt{$1}) {
+ &{$opt{$1}}();
+ $_ = $2;
+ next;
+ }
+ if (defined $opt_arg{$1}) {
+ if (! $2) {
+ $_ = shift @ARGV;
+ die("$0: -$1 option requires an argument.\n") if ! $_;
+ }
+ &{$opt_arg{$1}}($2 || $_);
+ return;
+ }
+ $_ =~ m/(..)(.*)/;
+ if (defined $opt_arg{$1}) {
+ if (! $2) {
+ $_ = shift @ARGV;
+ die("$0: -$1 option requires an argument.\n") if ! $_;
+ }
+ &{$opt_arg{$1}}($2 || $_);
+ return;
+ }
+ if ($_) {
+ die qq($0: unrecognized option "-$_". Use -x for a usage message.\n);
+ }
+ }
+}
+
+# Process an equate argument (var=val).
+sub equate {
+ my($var, $val) = /([^=]*)=(.*)/;
+ $script::ARG{$var} = $val;
+}
+
+# Define file signature protocol.
+'sig'->select($param::sigpro);
+
+# Cleanup after an interrupt.
+$SIG{INT} = $SIG{QUIT} = $SIG{TERM} = sub {
+ $SIG{PIPE} = $SIG{INT} = $SIG{QUIT} = $SIG{TERM} = 'IGNORE';
+ $SIG{HUP} = $SIG{INT} if ! $main::_WIN32;
+ warn("\n$0: killed\n");
+ # Call this first, to make sure that this processing
+ # occurs even if a child process does not die (and we
+ # hang on the wait).
+ sig::hash::END();
+ wait();
+ exit(1);
+};
+$SIG{HUP} = $SIG{INT} if ! $main::_WIN32;
+
+# Cleanup after a broken pipe (someone piped our stdout?)
+$SIG{PIPE} = sub {
+ $SIG{PIPE} = $SIG{HUP} = $SIG{INT} = $SIG{QUIT} = $SIG{TERM} = 'IGNORE';
+ warn("\n$0: broken pipe\n");
+ sig::hash::END();
+ wait();
+ exit(1);
+};
+
+if ($param::depfile) {
+ open (main::DEPFILE, ">".$param::depfile) ||
+ die ("$0: couldn't open $param::depfile ($!)\n");
+}
+
+# If the supplied top-level Conscript file is not in the
+# current directory, then change to that directory.
+{
+ my ($vol, $dir, $file) =
+ File::Spec->splitpath(File::Spec->canonpath($param::topfile));
+ if ($vol || $dir) {
+ my($cd) = File::Spec->catpath($vol, $dir, undef);
+ chdir($cd) || die("$0: couldn't change to directory $cd ($!)\n");
+ $param::topfile = $file;
+ }
+}
+
+# Walk up the directory hierarchy looking for a Conscript file (if -t set).
+my($target_top);
+my(@targetdir) = ();
+if ($param::traverse && ! -f $param::topfile) {
+ my($vol, $dirs, $file) = File::Spec->splitpath(cwd());
+ my(@dirs) = (File::Spec->splitdir($dirs), $file);
+ while (! -f File::Spec->catpath($vol, File::Spec->catdir(@dirs),
+ $param::topfile)) {
+ die("$0: unable to find $param::topfile.\n") if ! @dirs;
+ unshift(@targetdir, pop(@dirs));
+ }
+ my($cwd) = File::Spec->catpath($vol, File::Spec->catdir(@dirs), '');
+ print "$0: Entering directory `$cwd'\n";
+ chdir($cwd);
+ @targets = map {File::Spec->catdir(@targetdir, $_)} @targets;
+}
+
+# Set up $dir::top and $dir::cwd, now that we are in the right directory.
+dir::init();
+
+#
+if (@targetdir) {
+ $target_top = $dir::top->lookupdir(File::Spec->catdir(@targetdir));
+}
+
+# Now handle override file.
+package override;
+if ($param::overfile) {
+ my($ov) = $param::overfile;
+ die qq($0: can\'t read override file "$ov" ($!)\n) if ! -f $ov; #'
+ do $ov;
+ if ($@) {
+ chop($@);
+ die qq($0: errors in override file "$ov" ($@)\n);
+ }
+}
+
+# Provide this to user to setup override patterns.
+sub Override {
+ my($re, @env) = @_;
+ return if $param::overrides{$re}; # if identical, first will win.
+ $param::overrides = 1;
+ $param::overrides{$re} = \@env;
+ push(@param::overrides, $re);
+}
+
+package main;
+
+use vars qw( %priority $errors );
+
+# Check script inclusion regexps
+my $re;
+for $re (@param::include) {
+ if (! defined eval {"" =~ /$re/}) {
+ my($err) = $@;
+ $err =~ s/in regexp at .*$//;
+ die("$0: error in regexp $err");
+ }
+}
+
+# Read the top-level construct file and its included scripts.
+doscripts($param::topfile);
+
+# Status priorities. This lets us aggregate status for directories
+# and print an appropriate message (at the top-level).
+%priority =
+ ('none' => 1, 'handled' => 2, 'built' => 3, 'unknown' => 4, 'errors' => 5);
+
+# If no targets were specified, supply default targets (if any).
+@targets = @param::default_targets if ! @targets;
+
+$errors = 0;
+
+# Build the supplied target patterns.
+my $tgt;
+for $tgt (map($dir::top->lookup($_), @targets)) {
+ if ($target_top && ! $tgt->is_under($target_top)) {
+ # A -t option was used, and this target is not underneath
+ # the directory where we were invoked via -t.
+ # If the target is a directory and the -t directory
+ # is underneath it, then build the -t directory.
+ if (ref $tgt ne "dir" || ! $target_top->is_under($tgt)) {
+ next;
+ }
+ $tgt = $target_top;
+ }
+ buildtoptarget($tgt);
+}
+
+exit 0 + ($errors != 0);
+
+sub buildtoptarget {
+ my($tgt) = @_;
+ return if ! $tgt;
+ my($status) = buildtarget($tgt);
+ if ($status ne 'built') {
+ my($path) = $tgt->path;
+ if ($status eq "errors") {
+ print qq($0: "$path" not remade because of errors.\n);
+ $errors++;
+ } elsif ($status eq "handled") {
+ print qq($0: "$path" is up-to-date.\n) if ($param::quiet < 2);
+ } elsif ($status eq "unknown") {
+ # cons error already reported.
+ $errors++;
+ } elsif ($status eq "none") {
+ # search for targets that may be linked to the given path.
+ my @linked = dir::linked_targets($tgt) if $target_top;
+ if (@linked) {
+ my @names = map($_->path, @linked);
+ print "Linked targets: @names\n" if ($param::quiet < 1);
+ map(buildtoptarget($_), @linked);
+ } else {
+ print qq($0: nothing to be built in "$path".\n)
+ if $param::build && ($param::quiet < 2);
+ }
+ } else {
+ print qq($0: don\'t know how to construct "$path".\n); #'
+ $errors++;
+ }
+ }
+}
+
+# Build the supplied target directory or files. Return aggregated status.
+sub buildtarget {
+ my($tgt) = @_;
+ if (ref($tgt) eq "dir") {
+ my($result) = "none";
+ my($priority) = $priority{$result};
+ if (exists $tgt->{member}) {
+ my($members) = $tgt->{member};
+ my $entry;
+ for $entry (sort keys %$members) {
+ next if $entry eq $dir::CURDIR || $entry eq $dir::UPDIR;
+ my($tgt) = $members->{$entry};
+ next if ref($tgt) ne "dir" && !exists($tgt->{builder});
+ my($stat) = buildtarget($members->{$entry});
+ my($pri) = $priority{$stat};
+ if ($pri > $priority) {
+ $priority = $pri;
+ $result = $stat;
+ }
+ }
+ }
+ return $result;
+ }
+ if ($param::depends) {
+ my($path) = $tgt->path;
+ if ($tgt->{builder}) {
+ my(@dep) = (@{$tgt->{dep}}, @{$tgt->{sources}});
+ my($dep) = join(' ',map($_->path, @dep));
+ print("Target $path: $dep\n");
+ } else {
+ print("Target $path: not a derived file\n");
+ }
+ }
+ if ($param::build) {
+ return build $tgt;
+ } elsif ($param::pflag || $param::wflag || $param::aflag) {
+ if ($tgt->{builder}) {
+ if ($param::wflag) {
+ print qq(${\$tgt->path}: $tgt->{script}\n);
+ } elsif ($param::pflag) {
+ print qq(${\$tgt->path}:\n) if $param::aflag;
+ print qq(${\$tgt->path}\n) if !$param::aflag;
+ }
+ if ($param::aflag) {
+ $tgt->{builder}->action($tgt);
+ }
+ }
+ } elsif ($param::rflag && $tgt->{builder}) {
+ my($path) = $tgt->path;
+ if (-f $path) {
+ if (unlink($path)) {
+ print("Removed $path\n") if ($param::quiet < 1);
+ } else {
+ warn("$0: couldn't remove $path\n");
+ }
+ }
+ }
+
+ return "none";
+}
+
+package NameSpace;
+
+# Return a hash that maps the name of symbols in a namespace to an
+# array of refs for all types for which the name has a defined value.
+# A list of symbols may be specified; default is all symbols in the
+# name space.
+sub save {
+ my $package = shift;
+ my(%namerefs, $var, $type);
+ no strict 'refs';
+ @_ = keys %{$package."::"} if ! @_;
+ foreach $var (@_) {
+ $namerefs{$var} = [];
+ my $fqvar = $package."::".$var;
+ # If the scalar for this variable name doesn't already
+ # exist, *foo{SCALAR} will autovivify the reference
+ # instead of returning undef, so unlike the other types,
+ # we have to dereference to find out if it exists.
+ push(@{$namerefs{$var}}, *{$fqvar}{SCALAR})
+ if defined ${*{$fqvar}{SCALAR}};
+ foreach $type (qw(ARRAY HASH CODE IO)) {
+ push(@{$namerefs{$var}}, *{$fqvar}{$type})
+ if defined *{$fqvar}{$type};
+ }
+ }
+ return \%namerefs;
+}
+
+# Remove the specified symbols from the namespace.
+# Default is to remove all.
+sub remove {
+ my $package = shift;
+ my(%namerefs, $var);
+ no strict 'refs';
+ @_ = keys %{$package."::"} if ! @_;
+ foreach $var (@_) {
+ delete ${$package."::"}{$var};
+ }
+}
+
+# Restore values to symbols specified in a hash as returned
+# by NameSpace::save.
+sub restore {
+ my($package, $namerefs) = @_;
+ my($var, $ref);
+ no strict 'refs';
+ foreach $var (keys %$namerefs) {
+ my $fqvar = $package."::".$var;
+ foreach $ref (@{$namerefs->{$var}}) {
+ *{$fqvar} = $ref;
+ }
+ }
+}
+
+# Support for "building" scripts, importing and exporting variables.
+# With the exception of the top-level routine here (invoked from the
+# main package by cons), these are all invoked by user scripts.
+package script;
+
+use vars qw( $ARG $caller_dir_path %special_var );
+
+BEGIN {
+ # We can't Export or Import the following variables because Perl always
+ # treats them as part of the "main::" package (see perlvar(1)).
+ %special_var = map {$_ => 1} qw(ENV INC ARGV ARGVOUT SIG
+ STDIN STDOUT STDERR);
+}
+
+# This is called from main to interpret/run the top-level Construct
+# file, passed in as the single argument.
+sub main::doscripts {
+ my($script) = @_;
+ Build($script);
+ # Now set up the includes/excludes (after the Construct file is read).
+ $param::include = join('|', @param::include);
+
+ # Save the original variable names from the script package.
+ # These will stay intact, but any other "script::" variables
+ # defined in a Conscript file will get saved, deleted,
+ # and (when necessary) restored.
+ my(%orig_script_var) = map {$_ => 1} keys %script::;
+ $caller_dir_path = undef;
+ my $cwd = Cwd::cwd();
+ my(@scripts) = pop(@priv::scripts);
+ while ($priv::self = shift(@scripts)) {
+ my($path) = $priv::self->{script}->rsrcpath;
+ if (-f $path) {
+ $dir::cwd = $priv::self->{script}->{dir};
+ # Handle chdir to the Conscript file directory, if necessary.
+ my ($vol, $dir, $file);
+ if ($param::conscript_chdir) {
+ ($vol, $dir, $file) =
+ File::Spec->splitpath(File::Spec->canonpath($path));
+ if ($vol ne '' || $dir ne '') {
+ $caller_dir_path = File::Spec->catpath($vol, $dir, undef);
+ chdir($caller_dir_path) ||
+ die "Could not chdir to $caller_dir_path: $!\n";
+ }
+ } else {
+ $file = $path;
+ }
+ # Actually process the Conscript file.
+ do $file;
+ # Save any variables defined by the Conscript file
+ # so we can restore them later, if needed;
+ # then delete them from the script:: namespace.
+ my(@del) = grep(! $orig_script_var{$_}, keys %script::);
+ if (@del) {
+ $priv::self->{script}->{pkgvars} = NameSpace::save('script',
+ @del);
+ NameSpace::remove('script', @del);
+ }
+ if ($caller_dir_path) {
+ chdir($cwd);
+ $caller_dir_path = undef;
+ }
+ if ($@) {
+ chomp($@);
+ my $err = ($@ =~ /\n/ms) ? ":\n$@" : " ($@)";
+ print qq($0: error in file "$path"$err\n);
+ $run::errors++;
+ } else {
+ # Only process subsidiary scripts if no errors in parent.
+ unshift(@scripts, @priv::scripts);
+ }
+ undef @priv::scripts;
+ } else {
+ my $where = '';
+ my $cref = $priv::self->{script}->creator;
+ if (defined $cref) {
+ my($_foo, $script, $line, $sub) = @$cref;
+ $where = " ($sub in $script, line $line)";
+ }
+ warn qq(Ignoring missing script "$path"$where);
+ }
+ }
+ die("$0: script errors encountered: construction aborted\n")
+ if $run::errors;
+}
+
+# Return caller info about the method being invoked.
+# This is everything from the Perl "caller" builtin function,
+# including which Construct/Conscript file, line number,
+# subroutine name, etc.
+sub caller_info {
+ my($lev) = 1;
+ my(@frame);
+ do {
+ @frame = caller ++$lev;
+ if (defined($frame[3]) && $frame[3] eq '(eval)') {
+ @frame = caller --$lev;
+ if ($caller_dir_path) {
+ $frame[1] = File::Spec->catfile($caller_dir_path, $frame[1]);
+ }
+ return @frame;
+ }
+ } while ($frame[3]);
+ return;
+}
+
+# Link a directory to another. This simply means set up the *source*
+# for the directory to be the other directory.
+sub Link {
+ dir::link(@_);
+}
+
+# Add directories to the repository search path for files.
+# Strip our current directory from the list so Repository
+# (or -R options) can be used from within the repository.
+sub Repository {
+ my($my_dir) = Cwd::cwd();
+ my $dir;
+ foreach $dir (@_) {
+ # The following more direct call isn't available in
+ # Cwd.pm until some time after 5.003...
+ # my($d) = Cwd::abs_path($dir);
+ chdir($dir);
+ my($d) = Cwd::cwd();
+ chdir($my_dir);
+ #
+ next if ! $d || ! -d $d || $d eq $my_dir;
+ # We know we can get away with passing undef to lookupdir
+ # as the directory because $dir is an absolute path.
+ push(@param::rpath, dir::lookupdir(undef, $dir));
+ push @INC, $d;
+ }
+}
+
+# Return the list of Repository directories specified.
+sub Repository_List {
+ map($_->path, @param::rpath);
+}
+
+# Specify whether the .consign signature times in repository files are,
+# in fact, consistent with the times on the files themselves.
+sub Repository_Sig_Times_OK {
+ $param::rep_sig_times_ok = shift;
+}
+
+sub SourceSignature {
+ $param::sourcesig = [@_];
+}
+
+# Specify whether we should chdir to the containing directories
+# of Conscript files.
+sub Conscript_chdir {
+ $param::conscript_chdir = shift;
+}
+
+# Specify files/targets that must be present and built locally,
+# even if they exist already-built in a Repository.
+sub Local {
+ my(@files) = map($dir::cwd->lookupfile($_), @_);
+ map($_->local(1), @files);
+}
+
+# Export variables to any scripts invoked from this one.
+sub Export {
+ my(@illegal) = grep($special_var{$_}, @_);
+ if (@illegal) {
+ die qq($0: cannot Export special Perl variables: @illegal\n);
+ }
+ @{$priv::self->{exports}} = grep(! defined $special_var{$_}, @_);
+}
+
+# Import variables from the export list of the caller
+# of the current script.
+sub Import {
+ my(@illegal) = grep($special_var{$_}, @_);
+ if (@illegal) {
+ die qq($0: cannot Import special Perl variables: @illegal\n);
+ }
+ my($parent) = $priv::self->{parent};
+ my($imports) = $priv::self->{imports};
+ @{$priv::self->{exports}} = keys %$imports;
+ my($var);
+ foreach $var (grep(! defined $special_var{$_}, @_)) {
+ if (!exists $imports->{$var}) {
+ my($path) = $parent->{script}->path;
+ die qq($0: variable "$var" not exported by file "$path"\n);
+ }
+ if (!defined $imports->{$var}) {
+ my $path = $parent->{script}->path;
+ my $err = "$0: variable \"$var\" exported but not " .
+ "defined by file \"$path\"\n";
+ die $err;
+ }
+ ${"script::$var"} = $imports->{$var};
+ }
+}
+
+# Build an inferior script. That is, arrange to read and execute
+# the specified script, passing to it any exported variables from
+# the current script.
+sub Build {
+ my(@files) = map($dir::cwd->lookupfile($_), @_);
+ my(%imports) = map {$_ => ${"script::$_"}} @{$priv::self->{exports}};
+ my $file;
+ for $file (@files) {
+ next if $param::include && $file->path !~ /$param::include/o;
+ my($self) = {'script' => $file,
+ 'parent' => $priv::self,
+ 'imports' => \%imports};
+ bless $self; # may want to bless into class of parent in future
+ push(@priv::scripts, $self);
+ }
+}
+
+# Set up regexps dependencies to ignore. Should only be called once.
+sub Ignore {
+ die("Ignore called more than once\n") if $param::ignore;
+ $param::ignore = join("|", map("($_)", @_)) if @_;
+}
+
+# Specification of default targets.
+sub Default {
+ push(@param::default_targets, map($dir::cwd->lookup($_)->path, @_));
+}
+
+# Local Help. Should only be called once.
+sub Help {
+ if ($param::localhelp) {
+ print "@_\n";
+ exit 2;
+ }
+}
+
+# For windows platforms which use unix tool sets, the msvc defaults may
+# not be useful. Also, in the future, other platforms (Mac?) may have the
+# same problem.
+sub RuleSet {
+ my $style = shift;
+ my @rulesets = sort keys %param::rulesets;
+ die "Unknown style for rules: $style.\n" .
+ "Supported rules are: (" . join(" ", @rulesets) . ")"
+ unless eval(join("||", map("\$style eq '$_'", @rulesets)));
+ return @param::base, @{$param::rulesets{$style}};
+}
+
+sub DefaultRules {
+ @param::defaults = ();
+ push @param::defaults, @_;
+}
+
+# Return the build name(s) of a file or file list.
+sub FilePath {
+ wantarray
+ ? map($dir::cwd->lookupfile($_)->path, @_)
+ : $dir::cwd->lookupfile($_[0])->path;
+}
+
+# Return the build name(s) of a directory or directory list.
+sub DirPath {
+ wantarray
+ ? map($dir::cwd->lookupdir($_)->path, @_)
+ : $dir::cwd->lookupdir($_[0])->path;
+}
+
+# Split the search path provided into components. Look each up
+# relative to the current directory.
+# The usual path separator problems abound; for now we'll use :
+sub SplitPath {
+ my($dirs) = @_;
+ if (ref($dirs) ne "ARRAY") {
+ $dirs = [ split(/$main::PATH_SEPARATOR/o, $dirs) ];
+ }
+ map { DirPath($_) } @$dirs;
+}
+
+# Return true if the supplied path is available as a source file
+# or is buildable (by rules seen to-date in the build).
+sub ConsPath {
+ my($path) = @_;
+ my($file) = $dir::cwd->lookup($path);
+ return $file->accessible;
+}
+
+# Return the source path of the supplied path.
+sub SourcePath {
+ wantarray
+ ? map($dir::cwd->lookupfile($_)->rsrcpath, @_)
+ : $dir::cwd->lookupfile($_[0])->rsrcpath;
+}
+
+# Search up the tree for the specified cache directory, starting with
+# the current directory. Returns undef if not found, 1 otherwise.
+# If the directory is found, then caching is enabled. The directory
+# must be readable and writable. If the argument "mixtargets" is provided,
+# then targets may be mixed in the cache (two targets may share the same
+# cache file--not recommended).
+sub UseCache($@) {
+ my($dir, @args) = @_;
+ # NOTE: it's important to process arguments here regardless of whether
+ # the cache is disabled temporarily, since the mixtargets option affects
+ # the salt for derived signatures.
+ for (@args) {
+ if ($_ eq "mixtargets") {
+ # When mixtargets is enabled, we salt the target signatures.
+ # This is done purely to avoid a scenario whereby if
+ # mixtargets is turned on or off after doing builds, and
+ # if cache synchronization with -cs is used, then
+ # cache files may be shared in the cache itself (linked
+ # under more than one name in the cache). This is not bad,
+ # per se, but simply would mean that a cache cleaning algorithm
+ # that looked for a link count of 1 would never find those
+ # particular files; they would always appear to be in use.
+ $param::salt = 'M' . $param::salt;
+ $param::mixtargets = 1;
+ } else {
+ die qq($0: UseCache unrecognized option "$_"\n);
+ }
+ }
+ if ($param::cachedisable) {
+ warn("Note: caching disabled by -cd flag\n");
+ return 1;
+ }
+ my($depth) = 15;
+ while ($depth-- && ! -d $dir) {
+ $dir = File::Spec->catdir($dir::UPDIR, $dir);
+ }
+ if (-d $dir) {
+ $param::cache = $dir;
+ return 1;
+ }
+ return undef;
+}
+
+# Salt the signature generator. The salt (a number of string) is added
+# into the signature of each derived file. Changing the salt will
+# force recompilation of all derived files.
+sub Salt($) {
+ # We append the value, so that UseCache and Salt may be used
+ # in either order without changing the signature calculation.
+ $param::salt .= $_[0];
+}
+
+# Mark files (or directories) to not be removed before building.
+sub Precious {
+ map($_->{precious} = 1, map($dir::cwd->lookup($_), @_));
+}
+
+
+# These methods are callable from Conscript files, via a cons
+# object. Procs beginning with _ are intended for internal use.
+package cons;
+
+use vars qw( %envcache );
+
+# This is passed the name of the base environment to instantiate.
+# Overrides to the base environment may also be passed in
+# as key/value pairs.
+sub new {
+ my($package) = shift;
+ my ($env) = {@param::defaults, @_};
+ @{$env->{_envcopy}} = %$env; # Note: we never change PATH
+ $env->{_cwd} = $dir::cwd; # Save directory of environment for
+ bless $env, $package; # any deferred name interpretation.
+}
+
+# Clone an environment.
+# Note that the working directory will be the initial directory
+# of the original environment.
+sub clone {
+ my($env) = shift;
+ my $clone = {@{$env->{_envcopy}}, @_};
+ @{$clone->{_envcopy}} = %$clone; # Note: we never change PATH
+ $clone->{_cwd} = $env->{_cwd};
+ bless $clone, ref $env;
+}
+
+# Create a flattened hash representing the environment.
+# It also contains a copy of the PATH, so that the path
+# may be modified if it is converted back to a hash.
+sub copy {
+ my($env) = shift;
+ (@{$env->{_envcopy}}, 'ENV' => {%{$env->{ENV}}}, @_)
+}
+
+# Resolve which environment to actually use for a given
+# target. This is just used for simple overrides.
+sub _resolve {
+ return $_[0] if !$param::overrides;
+ my($env, $tgt) = @_;
+ my($path) = $tgt->path;
+ my $re;
+ for $re (@param::overrides) {
+ next if $path !~ /$re/;
+ # Found one. Return a combination of the original environment
+ # and the override.
+ my($ovr) = $param::overrides{$re};
+ return $envcache{$env,$re} if $envcache{$env,$re};
+ my($newenv) = {@{$env->{_envcopy}}, @$ovr};
+ @{$newenv->{_envcopy}} = %$env;
+ $newenv->{_cwd} = $env->{_cwd};
+ return $envcache{$env,$re} = bless $newenv, ref $env;
+ }
+ return $env;
+}
+
+# Substitute construction environment variables into a string.
+# Internal function/method.
+sub _subst {
+ my($env, $str) = @_;
+ if (! defined $str) {
+ return undef;
+ } elsif (ref($str) eq "ARRAY") {
+ return [ map($env->_subst($_), @$str) ];
+ } else {
+ # % expansion. %% gets converted to % later, so expand any
+ # %keyword construction that doesn't have a % in front of it,
+ # modulo multiple %% pairs in between.
+ # In Perl 5.005 and later, we could actually do this in one regex
+ # using a conditional expression as follows,
+ # while ($str =~ s/($pre)\%(\{)?([_a-zA-Z]\w*)(?(2)\})/"$1".
+ # $env->{$3}/ge) {}
+ # The following two-step approach is backwards-compatible
+ # to (at least) Perl5.003.
+ my $pre = '^|[^\%](?:\%\%)*';
+ while (($str =~ s/($pre)\%([_a-zA-Z]\w*)/$1.($env->{$2}||'')/ge) ||
+ ($str =~ s/($pre)\%\{([_a-zA-Z]\w*)\}/$1.($env->{$2}||'')/ge)) {
+ }
+ return $str;
+ }
+}
+
+sub AfterBuild {
+ my($env) = shift;
+ my($perl_eval_str) = pop(@_);
+ my $file;
+ for $file (map($dir::cwd->lookup($_), @_)) {
+ $file->{after_build_func} = $perl_eval_str;
+ }
+}
+
+sub Install {
+ my($env) = shift;
+ my($tgtdir) = $dir::cwd->lookupdir($env->_subst(shift));
+ my $file;
+ for $file (map($dir::cwd->lookupfile($env->_subst($_)), @_)) {
+ my($tgt) = $tgtdir->lookupfile($file->{entry});
+ $tgt->bind(find build::install($env), $file);
+ }
+}
+
+sub InstallAs {
+ my $env = shift;
+ my $tgt = shift;
+ my $src = shift;
+ my @sources = ();
+ my @targets = ();
+
+ if (ref $tgt) {
+ die "InstallAs: Source is a file and target is a list!\n"
+ if (!ref($src));
+ @sources = @$src;
+ @targets = @$tgt;
+ } elsif (ref $src) {
+ die "InstallAs: Target is a file and source is a list!\n";
+ } else {
+ push @sources, $src;
+ push @targets, $tgt;
+ }
+
+ if ($#sources != $#targets) {
+ my $tn = $#targets+1;
+ my $sn = $#sources+1;
+ die "InstallAs: Source file list ($sn) and target file list ($tn) " .
+ "are inconsistent in length!\n";
+ } else {
+ foreach (0..$#sources) {
+ my $tfile = $dir::cwd->lookupfile($env->_subst($targets[$_]));
+ my $sfile = $dir::cwd->lookupfile($env->_subst($sources[$_]));
+ $tfile->bind(find build::install($env), $sfile);
+ }
+ }
+}
+
+# Installation in a local build directory,
+# copying from the repository if it's already built there.
+# Functionally equivalent to:
+# Install $env $dir, $file;
+# Local "$dir/$file";
+sub Install_Local {
+ my($env) = shift;
+ my($tgtdir) = $dir::cwd->lookupdir($env->_subst(shift));
+ my $file;
+ for $file (map($dir::cwd->lookupfile($env->_subst($_)), @_)) {
+ my($tgt) = $tgtdir->lookupfile($file->{entry});
+ $tgt->bind(find build::install($env), $file);
+ $tgt->local(1);
+ }
+}
+
+sub Objects {
+ my($env) = shift;
+ map($dir::cwd->relpath($_), $env->_Objects(@_));
+}
+
+# Called with multiple source file references (or object files).
+# Returns corresponding object files references.
+sub _Objects {
+ my($env) = shift;
+ my($suffix) = $env->{SUFOBJ};
+ map($env->_Object($_, $_->{dir}->lookupfile($_->base_suf($suffix))),
+ map { ref $_ ? $_ : $dir::cwd->lookupfile($env->_subst($_)) }
+ grep(defined $_, @_));
+}
+
+# Called with an object and source reference. If no object reference
+# is supplied, then the object file is determined implicitly from the
+# source file's extension. Sets up the appropriate rules for creating
+# the object from the source. Returns the object reference.
+sub _Object {
+ my($env, $src, $obj) = @_;
+ return $obj if $src eq $obj; # don't need to build self from self.
+ my($objenv) = $env->_resolve($obj);
+ my($suffix) = $src->suffix;
+
+ my($builder) = $env->{SUFMAP}{$suffix};
+
+ if ($builder) {
+ $obj->bind((find $builder($objenv)), $src);
+ } else {
+ die("don't know how to construct ${\$obj->path} from " .
+ "${\$src->path}.\n");
+ }
+ $obj
+}
+
+sub Program {
+ my($env) = shift;
+ my($tgt) = $dir::cwd->lookupfile(file::addsuffix($env->_subst(shift),
+ $env->{SUFEXE}));
+ my($progenv) = $env->_resolve($tgt);
+ $tgt->bind(find build::command::link($progenv, $progenv->{LINKCOM}),
+ $env->_Objects(@_));
+}
+
+sub Module {
+ my($env) = shift;
+ my($tgt) = $dir::cwd->lookupfile($env->_subst(shift));
+ my($modenv) = $env->_resolve($tgt);
+ my($com) = pop(@_);
+ $tgt->bind(find build::command::link($modenv, $com), $env->_Objects(@_));
+}
+
+sub LinkedModule {
+ my($env) = shift;
+ my($tgt) = $dir::cwd->lookupfile($env->_subst(shift));
+ my($progenv) = $env->_resolve($tgt);
+ $tgt->bind(find build::command::linkedmodule
+ ($progenv, $progenv->{LINKMODULECOM}),
+ $env->_Objects(@_));
+}
+
+sub Library {
+ my($env) = shift;
+ my($lib) = $dir::cwd->lookupfile(file::addsuffix($env->_subst(shift),
+ $env->{SUFLIB}));
+ my($libenv) = $env->_resolve($lib);
+ $lib->bind(find build::command::library($libenv), $env->_Objects(@_));
+}
+
+# Simple derivation: you provide target, source(s), command.
+# Special variables substitute into the rule.
+# Target may be a reference, in which case it is taken
+# to be a multiple target (all targets built at once).
+sub Command {
+ my($env) = shift;
+ my($tgt) = $env->_subst(shift);
+ my($builder) = find build::command::user($env, pop(@_), 'script');
+ my(@sources) = map($dir::cwd->lookupfile($env->_subst($_)), @_);
+ if (ref($tgt)) {
+ # A multi-target command.
+ my(@tgts) = map($dir::cwd->lookupfile($_), @$tgt);
+ die("empty target list in multi-target command\n") if !@tgts;
+ $env = $env->_resolve($tgts[0]);
+ my($multi) = build::multiple->new($builder, \@tgts);
+ for $tgt (@tgts) {
+ $tgt->bind($multi, @sources);
+ }
+ } else {
+ $tgt = $dir::cwd->lookupfile($tgt);
+ $env = $env->_resolve($tgt);
+ $tgt->bind($builder, @sources);
+ }
+}
+
+sub Depends {
+ my($env) = shift;
+ my($tgt) = $env->_subst(shift);
+ my(@deps) = map($dir::cwd->lookup($env->_subst($_)), @_);
+ if (! ref($tgt)) {
+ $tgt = [ $tgt ];
+ }
+ my($t);
+ foreach $t (map($dir::cwd->lookupfile($_), @$tgt)) {
+ push(@{$t->{dep}}, @deps);
+ }
+}
+
+# Setup a quick scanner for the specified input file, for the
+# associated environment. Any use of the input file will cause the
+# scanner to be invoked, once only. The scanner sees just one line at
+# a time of the file, and is expected to return a list of
+# dependencies.
+sub QuickScan {
+ my($env, $code, $file, $path) = @_;
+ $dir::cwd->lookup($env->_subst($file))->{'srcscan',$env} =
+ find scan::quickscan($code, $env, $env->_subst($path));
+}
+
+# Generic builder module. Just a few default methods. Every derivable
+# file must have a builder object of some sort attached. Usually
+# builder objects are shared.
+package build;
+
+use vars qw( %builder );
+
+# Every builder must now have at least an associated environment,
+# so we can find its sigarray and calculate the proper signature.
+sub find {
+ my($class, $env) = @_;
+ $builder{$env} || do {
+ my $self = { env => $env };
+ $builder{$env} = bless $self, $class;
+ }
+}
+
+# Null signature for dynamic includes.
+sub includes { () }
+
+# Null signature for build script.
+sub scriptsig { () }
+
+# Not compatible with any other builder, by default.
+sub compatible { 0 }
+
+
+# Builder module for the Install command.
+package build::install;
+
+use vars qw( @ISA );
+
+BEGIN { @ISA = qw(build) }
+
+# Caching not supported for Install: generally install is trivial anyway,
+# and we don't want to clutter the cache.
+sub cachin { undef }
+sub cachout { }
+
+# Do the installation.
+sub action {
+ my($self, $tgt) = @_;
+ my($src) = $tgt->{sources}[0];
+ main::showcom("Install ${\$src->rpath} as ${\$tgt->path}")
+ if ($param::install && $param::quiet < 1);
+ return unless $param::build;
+ futil::install($src->rpath, $tgt);
+ return 1;
+}
+
+
+# Builder module for generic UNIX commands.
+package build::command;
+
+use vars qw( @ISA %com );
+
+BEGIN { @ISA = qw(build) }
+
+sub find {
+ my($class, $env, $cmd, $package) = @_;
+ my($act) = action::new($env, $cmd);
+ $package ||= '';
+ $com{$env,$act,$package} || do {
+ my $self = { env => $env, act => $act, 'package' => $package };
+ $com{$env,$act,$package} = bless $self, $class;
+ }
+}
+
+# Default cache in function.
+sub cachin {
+ my($self, $tgt, $sig) = @_;
+ if (cache::in($tgt, $sig)) {
+ if ($param::cachecom) {
+ $self->{act}->show($self->{env}, $tgt);
+ } else {
+ printf("Retrieved %s from cache\n", $tgt->path)
+ if ($param::quiet < 1);
+ }
+ return 1;
+ }
+ return undef;
+}
+
+# Default cache out function.
+sub cachout {
+ my($self, $tgt, $sig) = @_;
+ cache::out($tgt, $sig);
+}
+
+# Build the target using the previously specified commands.
+sub action {
+ my($self, $tgt) = @_;
+ $self->{act}->execute($self->{env}, $tgt, $self->{'package'});
+}
+
+# Return script signature.
+sub scriptsig {
+ $_[0]->{act}->scriptsig
+}
+
+
+# Create a linked module.
+package build::command::link;
+
+use vars qw( @ISA );
+
+BEGIN { @ISA = qw(build::command) }
+
+# Find an appropriate linker.
+sub find {
+ my($class, $env, $command) = @_;
+ if (!exists $env->{_LDIRS}) {
+ my($ldirs) = '';
+ my($wd) = $env->{_cwd};
+ my($pdirs) = $env->{LIBPATH};
+ if (! defined $pdirs) {
+ $pdirs = [ ];
+ } elsif (ref($pdirs) ne 'ARRAY') {
+ $pdirs = [ split(/$main::PATH_SEPARATOR/o, $pdirs) ];
+ }
+ my($dir, $dpath);
+ for $dir (map($wd->lookupdir($env->_subst($_)), @$pdirs)) {
+ $dpath = $dir->path;
+ # Add the (presumably local) directory to the -L flags
+ # if we're not using repositories, the directory exists,
+ # or it's Linked to a source directory (that is, it *will*
+ # exist by the time the link occurs).
+ $ldirs .= " ".$env->{LIBDIRPREFIX}.$dpath.$env->{LIBDIRSUFFIX}
+ if ! @param::rpath || -d $dpath || $dir->is_linked;
+ next if File::Spec->file_name_is_absolute($dpath);
+ if (@param::rpath) {
+ my $d;
+ if ($dpath eq $dir::CURDIR) {
+ foreach $d (map($_->path, @param::rpath)) {
+ $ldirs .= " " . $env->{LIBDIRPREFIX} .
+ $d . $env->{LIBDIRSUFFIX};
+ }
+ } else {
+ my($rpath);
+ foreach $d (map($_->path, @param::rpath)) {
+ $rpath = File::Spec->catfile($d, $dpath);
+ $ldirs .= " ". $env->{LIBDIRPREFIX} .
+ $rpath . $env->{LIBDIRSUFFIX} if -d $rpath;
+ }
+ }
+ }
+ }
+ $env->{_LDIRS} = "%($ldirs%)";
+ }
+
+ # Introduce a new magic _LIBS symbol which allows to use the
+ # Unix-style -lNAME syntax for Win32 only. -lNAME will be replaced
+ # with %{PREFLIB}NAME%{SUFLIB}. <schwarze@isa.de> 1998-06-18
+
+ if ($main::_WIN32 && !exists $env->{_LIBS}) {
+ my $libs;
+ my $name;
+ for $name (split(' ', $env->_subst($env->{LIBS} || ''))) {
+ if ($name =~ /^-l(.*)/) {
+ $name = "$env->{PREFLIB}$1$env->{SUFLIB}";
+ }
+ $libs .= ' ' . $name;
+ }
+ $env->{_LIBS} = $libs ? "%($libs%)" : '';
+ }
+ bless find build::command($env, $command);
+}
+
+# Called from file::build. Make sure any libraries needed by the
+# environment are built, and return the collected signatures
+# of the libraries in the path.
+sub includes {
+ return $_[0]->{'bsig'} if exists $_[0]->{'bsig'};
+ my($self, $tgt) = @_;
+ my($env) = $self->{env};
+ my($ewd) = $env->{_cwd};
+ my $ldirs = $env->{LIBPATH};
+ if (! defined $ldirs) {
+ $ldirs = [ ];
+ } elsif (ref($ldirs) ne 'ARRAY') {
+ $ldirs = [ split(/$main::PATH_SEPARATOR/o, $ldirs) ];
+ }
+ my @lpath = map($ewd->lookupdir($_), @$ldirs);
+ my(@sigs);
+ my(@names);
+
+ # Pass %LIBS symbol through %-substituition
+ # <schwarze@isa.de> 1998-06-18
+ @names = split(' ', $env->_subst($env->{LIBS} || ''));
+ my $name;
+ for $name (@names) {
+ my ($lpath, @allnames);
+ if ($name =~ /^-l(.*)/) {
+ # -l style names are looked up on LIBPATH, using all
+ # possible lib suffixes in the same search order the
+ # linker uses (according to SUFLIBS).
+ # Recognize new PREFLIB symbol, which should be 'lib' on
+ # Unix, and empty on Win32. TODO: What about shared
+ # library suffixes? <schwarze@isa.de> 1998-05-13
+ @allnames = map("$env->{PREFLIB}$1$_",
+ split(/:/, $env->{SUFLIBS}));
+ $lpath = \@lpath;
+ } else {
+ @allnames = ($name);
+ # On Win32, all library names are looked up in LIBPATH
+ # <schwarze@isa.de> 1998-05-13
+ if ($main::_WIN32) {
+ $lpath = [$dir::top, @lpath];
+ }
+ else {
+ $lpath = [$dir::top];
+ }
+ }
+ my $dir;
+ DIR: for $dir (@$lpath) {
+ my $n;
+ for $n (@allnames) {
+ my($lib) = $dir->lookup_accessible($n);
+ if ($lib) {
+ last DIR if $lib->ignore;
+ if ((build $lib) eq 'errors') {
+ $tgt->{status} = 'errors';
+ return undef;
+ }
+ push(@sigs, 'sig'->signature($lib));
+ last DIR;
+ }
+ }
+ }
+ }
+ $self->{'bsig'} = 'sig'->collect(@sigs);
+}
+
+# Always compatible with other such builders, so the user
+# can define a single program or module from multiple places.
+sub compatible {
+ my($self, $other) = @_;
+ ref($other) eq "build::command::link";
+}
+
+# Link a program.
+package build::command::linkedmodule;
+
+use vars qw( @ISA );
+
+BEGIN { @ISA = qw(build::command) }
+
+# Always compatible with other such builders, so the user
+# can define a single linked module from multiple places.
+sub compatible {
+ my($self, $other) = @_;
+ ref($other) eq "build::command::linkedmodule";
+}
+
+# Builder for a C module
+package build::command::cc;
+
+use vars qw( @ISA );
+
+BEGIN { @ISA = qw(build::command) }
+
+sub find {
+ $_[1]->{_cc} || do {
+ my($class, $env) = @_;
+ my($cpppath) = $env->_subst($env->{CPPPATH});
+ my($cscanner) = find scan::cpp($env->{_cwd}, $cpppath);
+ $env->{_IFLAGS} = "%(" . $cscanner->iflags($env) . "%)";
+ my($self) = find build::command($env, $env->{CCCOM});
+ $self->{scanner} = $cscanner;
+ bless $env->{_cc} = $self;
+ }
+}
+
+# Invoke the associated C scanner to get signature of included files.
+sub includes {
+ my($self, $tgt) = @_;
+ $self->{scanner}->includes($tgt, $tgt->{sources}[0]);
+}
+
+# Builder for a C++ module
+package build::command::cxx;
+
+use vars qw( @ISA );
+
+BEGIN { @ISA = qw(build::command) }
+
+sub find {
+ $_[1]->{_cxx} || do {
+ my($class, $env) = @_;
+ my($cpppath) = $env->_subst($env->{CPPPATH});
+ my($cscanner) = find scan::cpp($env->{_cwd}, $cpppath);
+ $env->{_IFLAGS} = "%(" . $cscanner->iflags($env) . "%)";
+ my($self) = find build::command($env, $env->{CXXCOM});
+ $self->{scanner} = $cscanner;
+ bless $env->{_cxx} = $self;
+ }
+}
+
+# Invoke the associated C scanner to get signature of included files.
+sub includes {
+ my($self, $tgt) = @_;
+ $self->{scanner}->includes($tgt, $tgt->{sources}[0]);
+}
+
+# Builder for a user command (cons::Command). We assume that a user
+# command might be built and implement the appropriate dependencies on
+# the command itself (actually, just on the first word of the command
+# line).
+package build::command::user;
+
+use vars qw( @ISA );
+
+BEGIN { @ISA = qw(build::command) }
+
+sub includes {
+ my($self, $tgt) = @_;
+ my($sig) = '';
+
+ # Check for any quick scanners attached to source files.
+ my $dep;
+ for $dep (@{$tgt->{dep}}, @{$tgt->{sources}}) {
+ my($scanner) = $dep->{'srcscan',$self->{env}};
+ if ($scanner) {
+ $sig .= $scanner->includes($tgt, $dep);
+ }
+ }
+
+ # XXX Optimize this to not use ignored paths.
+ if (! exists $self->{_comsig}) {
+ my($env) = $self->{env};
+ $self->{_comsig} = '';
+ my($com, $dir);
+ com:
+ for $com ($self->{act}->commands) {
+ my($pdirs) = $env->{ENV}->{PATH};
+ if (! defined $pdirs) {
+ $pdirs = [ ];
+ } elsif (ref($pdirs) ne 'ARRAY') {
+ $pdirs = [ split(/$main::PATH_SEPARATOR/o, $pdirs) ];
+ }
+ for $dir (map($dir::top->lookupdir($_), @$pdirs)) {
+ my($prog) = $dir->lookup_accessible($com);
+ if ($prog) { # XXX Not checking execute permission.
+ if ((build $prog) eq 'errors') {
+ $tgt->{status} = 'errors';
+ return $sig;
+ }
+ next com if $prog->ignore;
+ $self->{_comsig} .= 'sig'->signature($prog);
+ next com;
+ }
+ }
+ }
+ }
+
+ return $self->{_comsig} . $sig
+}
+
+
+# Builder for a library module (archive).
+# We assume that a user command might be built and implement the
+# appropriate dependencies on the command itself.
+package build::command::library;
+
+use vars qw( @ISA );
+
+BEGIN { @ISA = qw(build::command) }
+
+sub find {
+ my($class, $env) = @_;
+ bless find build::command($env, $env->{ARCOM})
+}
+
+# Always compatible with other library builders, so the user
+# can define a single library from multiple places.
+sub compatible {
+ my($self, $other) = @_;
+ ref($other) eq "build::command::library";
+}
+
+# A multi-target builder.
+# This allows multiple targets to be associated with a single build
+# script, without forcing all the code to be aware of multiple targets.
+package build::multiple;
+
+sub new {
+ my($class, $builder, $tgts) = @_;
+ bless { 'builder' => $builder, 'env' => $builder->{env}, 'tgts' => $tgts };
+}
+
+sub scriptsig {
+ my($self, $tgt) = @_;
+ $self->{builder}->scriptsig($tgt);
+}
+
+sub includes {
+ my($self, $tgt) = @_;
+ $self->{builder}->includes($tgt);
+}
+
+sub compatible {
+ my($self, $tgt) = @_;
+ $self->{builder}->compatible($tgt);
+}
+
+sub cachin {
+ my($self, $tgt, $sig) = @_;
+ $self->{builder}->cachin($tgt, $sig);
+}
+
+sub cachout {
+ my($self, $tgt, $sig) = @_;
+ $self->{builder}->cachout($tgt, $sig);
+}
+
+sub action {
+ my($self, $invoked_tgt) = @_;
+ return $self->{built} if exists $self->{built};
+
+ # Make sure all targets in the group are unlinked before building any.
+ my($tgts) = $self->{tgts};
+ my $tgt;
+ for $tgt (@$tgts) {
+ futil::mkdir($tgt->{dir});
+ unlink($tgt->path) if ! $tgt->precious;
+ }
+
+ # Now do the action to build all the targets. For consistency
+ # we always call the action on the first target, just so that
+ # $> is deterministic.
+ $self->{built} = $self->{builder}->action($tgts->[0]);
+
+ # Now "build" all the other targets (except for the one
+ # we were called with). This guarantees that the signature
+ # of each target is updated appropriately. We force the
+ # targets to be built even if they have been previously
+ # considered and found to be OK; the only effect this
+ # has is to make sure that signature files are updated
+ # correctly.
+ for $tgt (@$tgts) {
+ if ($tgt ne $invoked_tgt) {
+ delete $tgt->{status};
+ 'sig'->invalidate($tgt);
+ build $tgt;
+ }
+ }
+
+ # Status of action.
+ $self->{built};
+}
+
+package action;
+
+sub new {
+ my($env, $act) = @_;
+ if (ref($act) eq 'CODE') {
+ return action::perl->new($act);
+ } else {
+ return action::command->new($env, $act);
+ }
+}
+
+package action::command;
+
+use vars qw( @ISA %cmd %_varopts $_varletters );
+
+BEGIN {
+ @ISA = $main::_WIN32 ? 'action::command::win32' : 'action::command::unix';
+
+ # Internal hash for processing variable options.
+ # f: return file part
+ # d: return directory part
+ # F: return file part, but strip any suffix
+ # b: return full path, but strip any suffix (a.k.a. return basename)
+ # s: return only the suffix (or an empty string, if no suffix is there)
+ # a: return the absolute path to the file
+ # S: return the absolute path to a Linked source file
+ %_varopts = (
+ 'f' => sub { return $_[0]->{entry}; },
+ 'd' => sub { return $_[0]->{dir}->path; },
+ 'F' => sub { my $subst = $_[0]->{entry};
+ $subst =~ s/\.[^\.]+$//;
+ return $subst; },
+ 'b' => sub { my $subst = $_[0]->path;
+ $subst =~ s/\.[^\.]+$//;
+ return $subst; },
+ 's' => sub { my $subst = $_[0]->{entry};
+ $subst =~ m/(\.[^\.]+)$/;
+ return $1; },
+ 'a' => sub { my $path = $_[0]->path;
+ if (! File::Spec->file_name_is_absolute($path)) {
+ $path = File::Spec->catfile(Cwd::cwd(), $path);
+ }
+ return $path; },
+ 'S' => sub { my $path = $_[0]->srcpath;
+ if (! File::Spec->file_name_is_absolute($path)) {
+ my $cwd = File::Spec->canonpath(Cwd::cwd());
+ $path = File::Spec->catfile($cwd, $path);
+ }
+ return $path; },
+ );
+
+ $_varletters = join('', keys %_varopts);
+}
+
+# Internal routine for processing variable options.
+# Options are specified in hash in the BEGIN block above.
+# no option: return path to file (relative to top,
+# or absolute if it's outside)
+sub _variant {
+ my($opt, $file) = @_;
+ $opt = '' if ! defined $opt;
+ if (defined $_varopts{$opt}) {
+ return &{$_varopts{$opt}}($file);
+ }
+ return $file->path;
+}
+
+sub new {
+ my($class, $env, $cmd) = @_;
+ $cmd = $env->_subst($cmd);
+ $cmd{$env,$cmd} || do {
+ # Remove unwanted bits from signature -- those bracketed by %( ... %)
+ my $sigs = $cmd;
+ my $sig = '';
+ if (ref($sigs) eq 'ARRAY') {
+ # This is an array of commands..
+ my $f;
+ foreach $f (@$sigs) {
+ $sig .= _strip($f);
+ }
+ } else {
+ $sig = _strip($sigs);
+ }
+ my $self = { cmd => $cmd, cmdsig => 'sig'->cmdsig($sig) };
+ $cmd{$env,$cmd} = bless $self, $class;
+ }
+}
+
+sub _strip {
+ my $sig = shift;
+ $sig =~ s/^\@\s*//mg;
+ while ($sig =~ s/%\(([^%]|%[^\(])*?%\)//g) { }
+ $sig;
+}
+
+sub scriptsig {
+ $_[0]->{cmdsig};
+}
+
+# Return an array of all the commands (first word on each line).
+sub commands {
+ my($self) = @_;
+ my(@cmds) = ();
+ my $com;
+ my $cmd = $self->{'cmd'};
+ my @allcoms;
+
+ push @allcoms, ref $cmd ? @{$cmd} : split(/\n/, $cmd);
+
+ for $com (@allcoms) {
+ $com =~ s/^\s*//;
+ $com =~ s/\s.*//;
+ next if ! $com; # blank line
+ push @cmds, $com;
+ }
+ @cmds;
+}
+
+# For the signature of a basic command, we don't bother
+# including the command itself. This is not strictly correct,
+# and if we wanted to be rigorous, we might want to insist
+# that the command was checked for all the basic commands
+# like gcc, etc. For this reason we don't have an includes
+# method.
+
+# Call this to get the command line script: an array of
+# fully substituted commands.
+sub getcoms {
+ my($self, $env, $tgt) = @_;
+ my(@coms);
+ my $com;
+ my @allcoms = ();
+ my $cmd = $self->{'cmd'};
+
+ push @allcoms, ref $cmd ? @{$cmd} : split(/\n/, $cmd);
+
+ for $com (@allcoms) {
+ my(@src) = (undef, @{$tgt->{sources}});
+ my(@src1) = @src;
+
+ next if $com =~ /^\s*$/;
+
+ # NOTE: we used to have a more elegant s//.../e solution
+ # for the items below, but this caused a bus error...
+
+ # Remove %( and %) -- those are only used to bracket parts
+ # of the command that we don't depend on.
+ $com =~ s/%[()]//g;
+
+ # Deal with %n, n=1,9 and variants.
+ while ($com =~ /%([1-9])(:([$_varletters]?))?/o) {
+ my($match) = $&;
+ my($src) = $src1[$1];
+ my($subst) = _variant($3, $src1[$1]->rfile);
+ undef $src[$1];
+ $com =~ s/$match/$subst/;
+ }
+
+ # Deal with %0 aka %> and variants.
+ while ($com =~ /%[0>](:([$_varletters]?))?/o) {
+ my($match) = $&;
+ my($subst) = _variant($2, $tgt);
+ $com =~ s/$match/$subst/;
+ }
+
+ # Deal with %< (all sources except %n's already used)
+ while ($com =~ /%<(:([$_varletters]?))?/o) {
+ my($match) = $&;
+ my @list = ();
+ foreach (@src) {
+ push(@list, _variant($2, $_->rfile)) if $_;
+ }
+ my($subst) = join(' ', @list);
+ $com =~ s/$match/$subst/;
+ }
+
+ # Deal with %[ %].
+ $com =~ s{%\[(.*?)%\]}{
+ my($func, @args) = grep { $_ ne '' } split(/\s+/, $1);
+ die("$0: \"$func\" is not defined.\n")
+ unless ($env->{$func});
+ &{$env->{$func}}(@args);
+ }gex;
+
+ # Convert left-over %% into %.
+ $com =~ s/%%/%/g;
+
+ # White space cleanup. XXX NO WAY FOR USER TO HAVE QUOTED SPACES
+ $com = join(' ', split(' ', $com));
+ next if $com =~ /^:/ && $com !~ /^:\S/;
+ push(@coms, $com);
+ }
+ @coms
+}
+
+# Build the target using the previously specified commands.
+sub execute {
+ my($self, $env, $tgt, $package) = @_;
+
+ if ($param::build) {
+ futil::mkdir($tgt->{dir});
+ unlink($tgt->path) if ! $tgt->precious;
+ }
+
+ # Set environment.
+ map(delete $ENV{$_}, keys %ENV);
+ %ENV = %{$env->{ENV}};
+
+ # Handle multi-line commands.
+ my $com;
+ for $com ($self->getcoms($env, $tgt)) {
+ if ($com !~ s/^\@\s*//) {
+ main::showcom($com);
+ }
+ next if ! $param::build;
+
+ if ($com =~ /^\[perl\]\s*/) {
+ my $perlcmd = $';
+ my $status;
+ {
+ # Restore the script package variables that were defined
+ # in the Conscript file that defined this [perl] build,
+ # so the code executes with the expected variables.
+ # Then actually execute (eval) the [perl] command to build
+ # the target, followed by cleaning up the name space
+ # by deleting the package variables we just restored.
+ my($pkgvars) = $tgt->{conscript}->{pkgvars};
+ NameSpace::restore($package, $pkgvars) if $pkgvars;
+ $status = eval "package $package; $perlcmd";
+ NameSpace::remove($package, keys %$pkgvars) if $pkgvars;
+ }
+ if (!defined($status)) {
+ warn "$0: *** Error during perl command eval: $@.\n";
+ return undef;
+ } elsif ($status == 0) {
+ warn "$0: *** Perl command returned $status "
+ . "(this indicates an error).\n";
+ return undef;
+ }
+ next;
+ }
+ if (! $self->do_command($com, $tgt->path)) {
+ return undef;
+ }
+ }
+
+ # success.
+ return 1;
+}
+
+sub show {
+ my($self, $env, $tgt) = @_;
+ my $com;
+ for $com ($self->getcoms($env, $tgt)) {
+ if ($com !~ /^\@\s*/) {
+ main::showcom($com);
+ }
+ }
+}
+
+package action::command::unix;
+
+sub do_command {
+ my($class, $com, $path) = @_;
+ my($pid) = fork();
+ die("$0: unable to fork child process ($!)\n") if !defined $pid;
+ if (!$pid) {
+ # This is the child. We eval the command to suppress -w
+ # warnings about not reaching the statements afterwards.
+ eval 'exec($com)';
+ $com =~ s/\s.*//;
+ die qq($0: failed to execute "$com" ($!). )
+ . qq(Is this an executable on path "$ENV{PATH}"?\n);
+ }
+ for (;;) {
+ do {} until wait() == $pid;
+ my ($b0, $b1) = ($? & 0xFF, $? >> 8);
+ # Don't actually see 0177 on stopped process; is this necessary?
+ next if $b0 == 0177; # process stopped; we can wait.
+ if ($b0) {
+ my($core, $sig) = ($b0 & 0200, $b0 & 0177);
+ my($coremsg) = $core ? "; core dumped" : "";
+ $com =~ s/\s.*//;
+ my $err = "$0: *** \[$path\] $com terminated by signal " .
+ "$sig$coremsg\n";
+ warn $err;
+ return undef;
+ }
+ if ($b1) {
+ warn qq($0: *** [$path] Error $b1\n); # trying to be like make.
+ return undef;
+ }
+ last;
+ }
+ return 1;
+}
+
+package action::command::win32;
+
+sub do_command {
+ my($class, $com, $path) = @_;
+ system($com);
+ if ($?) {
+ my ($b0, $b1) = ($? & 0xFF, $? >> 8);
+ my $err = $b1 || $?;
+ my $warn = qq($0: *** [$path] Error $err);
+ $warn .= " (executable not found in path?)" if $b1 == 0xFF;
+ warn "$warn\n";
+ return undef;
+ }
+ return 1;
+}
+
+package action::perl;
+
+# THIS IS AN EXPERIMENTAL PACKAGE. It's entirely possible that the
+# interface may change as this gets completed, so use at your own risk.
+#
+# There are (at least) two issues that need to be solved before blessing
+# this as a real, fully-supported feature:
+#
+# -- We need to calculate a signature value for a Perl code ref, in
+# order to rebuild the target if there's a change to the Perl code
+# used to generate it.
+#
+# This is not straightforward. A B::Deparse package exists that
+# decompiles a coderef into text. It's reportedly not completely
+# reliable for closures; it misses which variables are global, and
+# the values of private lexicals. Nevertheless, it'd probably
+# be perfect for our purposes, except that it wasn't added until
+# some time between Perl 5.00502 and 5.00554, and doesn't seem to
+# really work until Perl 5.6.0, so by relying on it, we'd lose
+# support for Perl versions back to 5.003*.
+#
+# -- Ideally, a code ref should be able to use something like
+# $env->_subst to fetch values from the construction environment
+# to modify its behavior without having to cut-and-paste code.
+# (Actually, since we pass the environment to the executed code
+# ref, there's no reason you can't do this with the code as it
+# stands today.) But this REALLY complicates the signature
+# calculation, because now the actual signature would depend not
+# just on the code contents, but on the construction variables (or
+# maybe just the environment).
+#
+# A potentially valid workaround would be to use the contents of the
+# Conscript file in which the code reference is defined as the code
+# ref's signature. This has the drawback of causing a recompilation of
+# the target file even in response to unrelated changes in the Conscript
+# file, but it would ensure correct builds without having to solve the
+# messy issues of generating a signature directly from a code ref.
+#
+# Nevertheless, this seemed a useful enough skeleton of a feature that
+# it made sense to release it in hopes that some practical experience
+# will encourage someone to figure out how to solve the signature
+# issues. Or maybe we'll discover these aren't big issues in practice
+# and end up blessing it as is.
+
+use vars qw( %code );
+
+sub new {
+ my($class, $cref) = @_;
+ $code{$cref} || do {
+ my $sig = '';
+ # Generating a code signature using B::Deparse doesn't really
+ # work for us until Perl 5.6.0. Here's the code in case
+ # someone wants to use it.
+ #use B::Deparse;
+ #my $deparse = B::Deparse->new();
+ #my $body = $deparse->coderef2text($cref);
+ #$sig = $body; # should be an MD5 sig
+ my($self) = { cref => $cref, crefsig => $sig };
+ $code{$cref} = bless $self, $class;
+ }
+}
+
+sub scriptsig {
+ $_[0]->{crefsig}
+}
+
+sub execute {
+ my($self, $env, $tgt) = @_;
+ if ($param::build) {
+ futil::mkdir($tgt->{dir});
+ unlink($tgt->path) if ! $tgt->precious;
+ my($cref) = $self->{cref};
+ &$cref($env, $tgt->path, map($_->rpath, @{$tgt->{sources}}));
+ }
+}
+
+sub commands {
+ return ();
+}
+
+
+# Generic scanning module.
+package scan;
+
+# Returns the signature of files included by the specified files on
+# behalf of the associated target. Any errors in handling the included
+# files are propagated to the target on whose behalf this processing
+# is being done. Signatures are cached for each unique file/scanner
+# pair.
+sub includes {
+ my($self, $tgt, @files) = @_;
+ my(%files, $file);
+ my($inc) = $self->{includes} || ($self->{includes} = {});
+ while ($file = pop @files) {
+ next if exists $files{$file};
+ if ($inc->{$file}) {
+ push(@files, @{$inc->{$file}});
+ $files{$file} = 'sig'->signature($file->rfile);
+ } else {
+ if ((build $file) eq 'errors') {
+ $tgt->{status} = 'errors'; # tgt inherits build status
+ return ();
+ }
+ $files{$file} = 'sig'->signature($file->rfile);
+ my(@includes) = $self->scan($file);
+ $inc->{$file} = \@includes;
+ push(@files, @includes);
+ }
+ }
+ 'sig'->collect(sort values %files)
+}
+
+
+# A simple scanner. This is used by the QuickScanfunction, to setup
+# one-time target and environment-independent scanning for a source
+# file. Only used for commands run by the Command method.
+package scan::quickscan;
+
+use vars qw( @ISA %scanner );
+
+BEGIN { @ISA = qw(scan) }
+
+sub find {
+ my($class, $code, $env, $pdirs) = @_;
+ if (! defined $pdirs) {
+ $pdirs = [ ] ;
+ } elsif (ref($pdirs) ne 'ARRAY') {
+ $pdirs = [ split(/$main::PATH_SEPARATOR/o, $pdirs) ];
+ }
+ my(@path) = map { $dir::cwd->lookupdir($_) } @$pdirs;
+ my($spath) = "@path";
+ $scanner{$code,$env,$spath} || do {
+ my($self) = { code => $code, env => $env, path => \@path };
+ $scanner{$code,$env,$spath} = bless $self;
+ }
+}
+
+# Scan the specified file for included file names.
+sub scan {
+ my($self, $file) = @_;
+ my($code) = $self->{code};
+ my(@includes);
+ # File should have been built by now. If not, we'll ignore it.
+ return () unless open(SCAN, $file->rpath);
+ while(<SCAN>) {
+ push(@includes, grep($_ ne '', &$code));
+ }
+ close(SCAN);
+ my($wd) = $file->{dir};
+ my(@files);
+ my $name;
+ for $name (@includes) {
+ my $dir;
+ for $dir ($file->{dir}, @{$self->{path}}) {
+ my($include) = $dir->lookup_accessible($name);
+ if ($include) {
+ push(@files, $include) unless $include->ignore;
+ last;
+ }
+ }
+ }
+ @files
+}
+
+
+# CPP (C preprocessor) scanning module
+package scan::cpp;
+
+use vars qw( @ISA %scanner );
+
+BEGIN { @ISA = qw(scan) }
+
+# For this constructor, provide the include path argument (colon
+# separated). Each path is taken relative to the provided directory.
+
+# Note: a particular scanning object is assumed to always return the
+# same result for the same input. This is why the search path is a
+# parameter to the constructor for a CPP scanning object. We go to
+# some pains to make sure that we return the same scanner object
+# for the same path: otherwise we will unecessarily scan files.
+sub find {
+ my($class, $dir, $pdirs) = @_;
+ if (! defined $pdirs) {
+ $pdirs = [ ];
+ } elsif (ref($pdirs) ne 'ARRAY') {
+ $pdirs = [ split(/$main::PATH_SEPARATOR/o, $pdirs) ];
+ }
+ my @path = map($dir->lookupdir($_), @$pdirs);
+ my($spath) = "@path";
+ $scanner{$spath} || do {
+ my($self) = {'path' => \@path};
+ $scanner{$spath} = bless $self;
+ }
+}
+
+# Scan the specified file for include lines.
+sub scan {
+ my($self, $file) = @_;
+ my($angles, $quotes);
+
+ if (exists $file->{angles}) {
+ $angles = $file->{angles};
+ $quotes = $file->{quotes};
+ } else {
+ my(@anglenames, @quotenames);
+ return () unless open(SCAN, $file->rpath);
+ while (<SCAN>) {
+ next unless /^\s*#/;
+ if (/^\s*#\s*include\s*([<"])(.*?)[>"]/) {
+ if ($1 eq "<") {
+ push(@anglenames, $2);
+ } else {
+ push(@quotenames, $2);
+ }
+ }
+ }
+ close(SCAN);
+ $angles = $file->{angles} = \@anglenames;
+ $quotes = $file->{quotes} = \@quotenames;
+ }
+
+
+ my(@shortpath) = @{$self->{path}}; # path for <> style includes
+ my(@longpath) = ($file->{dir}, @shortpath); # path for "" style includes
+
+ my(@includes);
+
+ my $name;
+ for $name (@$angles) {
+ my $dir;
+ for $dir (@shortpath) {
+ my($include) = $dir->lookup_accessible($name);
+ if ($include) {
+ push(@includes, $include) unless $include->ignore;
+ last;
+ }
+ }
+ }
+
+ for $name (@$quotes) {
+ my $dir;
+ for $dir(@longpath) {
+ my($include) = $dir->lookup_accessible($name);
+ if ($include) {
+ push(@includes, $include) unless $include->ignore;
+ last;
+ }
+ }
+ }
+
+ return @includes
+}
+
+# Return the include flags that would be used for a C Compile.
+sub iflags {
+ my($self, $env) = @_;
+ my($iflags) = '';
+ my($dir, $dpath);
+ for $dir (@{$self->{path}}) {
+ $dpath = $dir->path;
+ # Add the (presumably local) directory to the -I flags
+ # if we're not using repositories, the directory exists,
+ # or it's Linked to a source directory (that is, it *will*
+ # exist by the time the compilation occurs).
+ $iflags .= " ".$env->{INCDIRPREFIX}.$dpath.$env->{INCDIRSUFFIX}
+ if ! @param::rpath || -d $dpath || $dir->is_linked;
+ next if File::Spec->file_name_is_absolute($dpath);
+ if (@param::rpath) {
+ my $d;
+ if ($dpath eq $dir::CURDIR) {
+ foreach $d (map($_->path, @param::rpath)) {
+ $iflags .= " ".$env->{INCDIRPREFIX}.$d.$env->{INCDIRSUFFIX};
+ }
+ } else {
+ my($rpath);
+ foreach $d (map($_->path, @param::rpath)) {
+ $rpath = File::Spec->catfile($d, $dpath);
+ $iflags .= " ".$env->{INCDIRPREFIX}.$rpath.$env->{INCDIRSUFFIX}
+ if -d $rpath;
+ }
+ }
+ }
+ }
+ $iflags
+}
+
+package File::Spec;
+
+use vars qw( $_SEP $_MATCH_SEP $_MATCH_VOL );
+
+# Cons is migrating to using File::Spec for portable path name
+# manipulation. This is the right long-term direction, but there are
+# some problems with making the transition:
+#
+# For multi-volume support, we need to use newer interfaces
+# (splitpath, catpath, splitdir) that are only available in
+# File::Spec 0.8.
+#
+# File::Spec 0.8 doesn't work with Perl 5.00[34] due to
+# regular expression incompatibilities (use of \z).
+#
+# Forcing people to use a new version of a module is painful
+# because (in the workplace) their administrators aren't
+# always going to agree to install it everywhere.
+#
+# As a middle ground, we provide our own versions of all the File::Spec
+# methods we use, supporting both UNIX and Win32. Some of these methods
+# are home brew, some are cut-and-pasted from the real File::Spec methods.
+# This way, we're not reinventing the whole wheel, at least.
+#
+# We can (and should) get rid of this class whenever 5.00[34] and
+# versions of File::Spec prior to 0.9 (?) have faded sufficiently.
+# We also may need to revisit whenever someone first wants to use
+# Cons on some platform other than UNIX or Win32.
+
+BEGIN {
+ if ($main::_WIN32) {
+ $_SEP = '\\';
+ $_MATCH_SEP = "[\Q/$_SEP\E]";
+ $_MATCH_VOL = "([a-z]:)?$_MATCH_SEP";
+ } else {
+ $_SEP = '/';
+ $_MATCH_SEP = "\Q$_SEP\E";
+ $_MATCH_VOL = $_MATCH_SEP;
+ }
+}
+
+sub canonpath {
+ my ($self, $path) = @_;
+ if ($main::_WIN32) {
+ $path =~ s/^([a-z]:)/\u$1/s;
+ $path =~ s|/|\\|g;
+ $path =~ s|([^\\])\\+|$1\\|g; # xx////xx -> xx/xx
+ $path =~ s|(\\\.)+\\|\\|g; # xx/././xx -> xx/xx
+ $path =~ s|^(\.\\)+||s unless $path eq ".\\"; # ./xx -> xx
+ $path =~ s|\\$||
+ unless $path =~ m#^([A-Z]:)?\\$#s; # xx/ -> xx
+ } else {
+ $path =~ s|/+|/|g unless($^O eq 'cygwin'); # xx////xx -> xx/xx
+ $path =~ s|(/\.)+/|/|g; # xx/././xx -> xx/xx
+ $path =~ s|^(\./)+||s unless $path eq "./"; # ./xx -> xx
+ $path =~ s|^/(\.\./)+|/|s; # /../../xx -> xx
+ $path =~ s|/$|| unless $path eq "/"; # xx/ -> xx
+ }
+ return $path;
+}
+
+sub catdir {
+ my $self = shift;
+ my @args = @_;
+ foreach (@args) {
+ # append a slash to each argument unless it has one there
+ $_ .= $_SEP if $_ eq '' || substr($_,-1) ne $_SEP;
+ }
+ return $self->canonpath(join('', @args));
+}
+
+sub catfile {
+ my $self = shift;
+ my $file = pop @_;
+ return $file unless @_;
+ my $dir = $self->catdir(@_);
+ $dir .= $_SEP unless substr($dir,-1) eq $_SEP;
+ $file = '' if ! defined($file);
+ return $dir.$file;
+}
+
+sub catpath {
+ my $path = $_[1] . $_[0]->catfile(@_[2..$#_]);
+ $path =~ s/(.)$_MATCH_SEP*$/$1/;
+ $path;
+}
+
+sub curdir {
+ '.'
+}
+
+sub file_name_is_absolute {
+ my ($self, $file) = @_;
+ return scalar($file =~ m{^$_MATCH_VOL}is);
+}
+
+sub splitdir {
+ my @dirs = split(/$_MATCH_SEP/, $_[1], -1);
+ push(@dirs, '') if $dirs[$#dirs];
+ @dirs;
+}
+
+sub splitpath {
+ my ($self, $path) = @_;
+ my $vol = '';
+ my $sep = $_SEP;
+ if ($main::_WIN32) {
+ if ($path =~ s#^([A-Za-z]:|(?:\\\\|//)[^\\/]+[\\/][^\\/]+)([\\/])#$2#) {
+ $vol = $1;
+ $sep = $2;
+ }
+ }
+ my(@path) = split(/$_MATCH_SEP/, $path, -1);
+ my $file = pop @path;
+ my $dirs = join($sep, @path, '');
+ return ($vol, $dirs, $file);
+}
+
+sub updir {
+ '..'
+}
+
+sub case_tolerant {
+ return $main::_WIN32;
+}
+
+# Directory and file handling. Files/dirs are represented by objects.
+# Other packages are welcome to add component-specific attributes.
+package dir;
+
+use vars qw( $SEPARATOR $MATCH_SEPARATOR $CURDIR $UPDIR
+ $cwd_vol %root $top $cwd );
+
+BEGIN {
+ # A portable way of determing our directory separator.
+ $SEPARATOR = File::Spec->catdir('', '');
+ # A fast-path regular expression to match a directory separator
+ # anywhere in a path name.
+ if ($SEPARATOR eq '/') {
+ $MATCH_SEPARATOR = "\Q$SEPARATOR\E";
+ } else {
+ $MATCH_SEPARATOR = "[\Q/$SEPARATOR\E]";
+ }
+ # Cache these values so we don't have to make a method call
+ # every time we need them.
+ $CURDIR = File::Spec->curdir; # '.' on UNIX
+ $UPDIR = File::Spec->updir; # '..' on UNIX
+ #
+ $cwd_vol = '';
+}
+
+# Annotate a node (file or directory) with info about the
+# method that created it.
+sub creator {
+ my($self, @frame) = @_;
+ $self->{'creator'} = \@frame if @frame;
+ $self->{'creator'};
+}
+
+# Handle a file|dir type exception. We only die if we find we were
+# invoked by something in a Conscript/Construct file, because
+# dependencies created directly by Cons' analysis shouldn't cause
+# an error.
+sub _type_exception {
+ my($e) = @_;
+ my($line, $sub);
+ (undef, undef, $line, $sub) = script::caller_info;
+ if (defined $line) {
+ my $err = "\"${\$e->path}\" already in use as a " . ref($e) . " before $sub on line $line";
+ if ($e->{'creator'}) {
+ my $script;
+ (undef, $script, $line, $sub) = @{$e->{'creator'}};
+ $err = "\t" . $err . ",\n\t\tdefined by $sub in $script, line $line";
+ }
+ $err .= "\n";
+ die $err;
+ }
+}
+
+# This wraps up all the common File::Spec logic that we use for parsing
+# directory separators in a path and turning it into individual
+# subdirectories that we must create, as well as creation of root
+# nodes for any new file system volumes we find. File::Spec doesn't have
+# intuitively obvious interfaces, so this is heavily commented.
+#
+# Note: This is NOT an object or class method;
+# it's just a utility subroutine.
+sub _parse_path {
+ my($dir, $path) = @_;
+
+ # Convert all slashes to the native directory separator.
+ # This allows Construct files to always be written with good
+ # old POSIX path names, regardless of what we're running on.
+ $path = File::Spec->canonpath($path);
+
+ # File::Spec doesn't understand the Cons convention of
+ # an initial '#' for top-relative files. Strip it.
+ my($toprel) = $path =~ s/^#//;
+
+ # Let File::Spec do the heavy lifting of parsing the path name.
+ my($vol, $directories, $entry) = File::Spec->splitpath($path);
+ my @dirs = File::Spec->splitdir($directories);
+
+ # If there was a file entry on the end of the path, then the
+ # last @dirs element is '' and we don't need it. If there
+ # wasn't a file entry on the end (File::Spec->splitpath() knew
+ # the last component was a directory), then the last @dirs
+ # element becomes the entry we want to look up.
+ my($e) = pop @dirs;
+ $entry = $e if $entry eq '';
+
+ if (File::Spec->file_name_is_absolute($path)) {
+ # An absolute path name. If no volume was supplied,
+ # use the volume of our current directory.
+ $vol = $cwd_vol if $vol eq '';
+ $vol = uc($vol) if File::Spec->case_tolerant;
+ if (! defined $root{$vol}) {
+ # This is our first time looking up a path name
+ # on this volume, so create a root node for it.
+ # (On UNIX systems, $vol is always '', so '/'
+ # always maps to the $root{''} node.)
+ $root{$vol} = {path => $vol.$SEPARATOR,
+ prefix => $vol.$SEPARATOR,
+ srcpath => $vol.$SEPARATOR,
+ 'exists' => 1 };
+ $root{$vol}->{'srcdir'} = $root{$vol};
+ bless $root{$vol};
+ }
+ # We're at the top, so strip the blank entry from the front of
+ # the @dirs array since the initial '/' it represents will now
+ # be supplied by the root node we return.
+ shift @dirs;
+ $dir = $root{$vol};
+ } elsif ($toprel) {
+ $dir = $dir::top;
+ }
+ ($dir, \@dirs, $entry);
+}
+
+# Common subroutine for creating directory nodes.
+sub _create_dirs {
+ my ($dir, @dirs) = @_;
+ my $e;
+ foreach $e (@dirs) {
+ my $d = $dir->{member}->{$e};
+ if (! defined $d) {
+ bless $d = { 'entry' => $e, 'dir' => $dir, }, 'dir';
+ $d->creator(script::caller_info);
+ $d->{member}->{$dir::CURDIR} = $d;
+ $d->{member}->{$dir::UPDIR} = $dir;
+ $dir->{member}->{$e} = $d;
+ } elsif (ref $d eq 'entry') {
+ bless $d, 'dir';
+ $d->{member}->{$dir::CURDIR} = $d;
+ $d->{member}->{$dir::UPDIR} = $dir;
+ } elsif (ref $d eq 'file') {
+ # This clause is to supply backwards compatibility,
+ # with a warning, for anyone that's used FilePath
+ # to refer to a directory. After people have using
+ # 1.8 have had time to adjust (sometime in version
+ # 1.9 or later), we should remove this entire clause.
+ my($script, $line, $sub);
+ (undef, $script, $line, $sub) = @{$d->{'creator'}};
+ if ($sub eq 'script::FilePath') {
+ print STDERR "$0: Warning: $sub used to refer to a directory\n"
+ . "\tat line $line of $script. Use DirPath instead.\n";
+ bless $d, 'dir';
+ } else {
+ _type_exception($d);
+ }
+ } elsif (ref $d ne 'dir') {
+ _type_exception($d);
+ }
+ $dir = $d;
+ }
+ $dir;
+}
+
+# Look up an entry in a directory. This method is for when we don't
+# care whether a file or directory is returned, so if the entry already
+# exists, it will simply be returned. If not, we create it as a
+# generic "entry" which can be later turned into a file or directory
+# by a more-specific lookup.
+#
+# The file entry may be specified as relative, absolute (starts with /),
+# or top-relative (starts with #).
+sub lookup {
+ my($dir, $entry) = @_;
+
+ if ($entry !~ m#$MATCH_SEPARATOR#o) {
+ # Fast path: simple entry name in a known directory.
+ if ($entry =~ s/^#//) {
+ # Top-relative names begin with #.
+ $dir = $dir::top;
+ } elsif ($entry =~ s/^!//) {
+ $dir = $dir::cwd->srcdir;
+ }
+ } else {
+ my $dirsref;
+ ($dir, $dirsref, $entry) = _parse_path($dir, $entry);
+ $dir = _create_dirs($dir, @$dirsref) if @$dirsref;
+ return if ! defined $dir;
+ return $dir if $entry eq '';
+ }
+
+ my $e = $dir->{member}->{$entry};
+ if (! defined $e) {
+ bless $e = { 'entry' => $entry, 'dir' => $dir, }, 'entry';
+ $e->creator(script::caller_info);
+ $dir->{member}->{$entry} = $e;
+ }
+
+ $e;
+}
+
+# Look up a file entry in a directory.
+#
+# The file entry may be specified as relative, absolute (starts with /),
+# or top-relative (starts with #).
+sub lookupfile {
+ my($dir, $entry) = @_;
+
+ if ($entry !~ m#$MATCH_SEPARATOR#o) {
+ # Fast path: simple entry name in a known directory.
+ if ($entry =~ s/^#//) {
+ # Top-relative names begin with #.
+ $dir = $dir::top;
+ } elsif ($entry =~ s/^!//) {
+ $dir = $dir::cwd->srcdir;
+ }
+ } else {
+ my $dirsref;
+ ($dir, $dirsref, $entry) = _parse_path($dir, $entry);
+ $dir = _create_dirs($dir, @$dirsref) if @$dirsref;
+ return undef if $entry eq '';
+ }
+
+ my $f = $dir->{member}->{$entry};
+ if (! defined $f) {
+ bless $f = { 'entry' => $entry, 'dir' => $dir, }, 'file';
+ $f->creator(script::caller_info);
+ $dir->{member}->{$entry} = $f;
+ } elsif (ref $f eq 'entry') {
+ bless $f, 'file';
+ } elsif (ref $f ne 'file') {
+ _type_exception($f);
+ }
+
+ $f;
+}
+
+# Look up a (sub-)directory entry in a directory.
+#
+# The (sub-)directory entry may be specified as relative, absolute
+# (starts with /), or top-relative (starts with #).
+sub lookupdir {
+ my($dir, $entry) = @_;
+
+ my $dirsref;
+ if ($entry !~ m#$MATCH_SEPARATOR#o) {
+ # Fast path: simple entry name in a known directory.
+ if ($entry =~ s/^#//) {
+ # Top-relative names begin with #.
+ $dir = $dir::top;
+ } elsif ($entry =~ s/^!//) {
+ $dir = $dir::cwd->srcdir;
+ }
+ } else {
+ ($dir, $dirsref, $entry) = _parse_path($dir, $entry);
+ }
+ push(@$dirsref, $entry) if $entry ne '';
+ _create_dirs($dir, @$dirsref);
+}
+
+# Look up a file entry and return it if it's accessible.
+sub lookup_accessible {
+ my $file = $_[0]->lookupfile($_[1]);
+ return ($file && $file->accessible) ? $file : undef;
+}
+
+# Return the parent directory without doing a lookupdir,
+# which would create a parent if it doesn't already exist.
+# A return value of undef (! $dir->up) indicates a root directory.
+sub up {
+ $_[0]->{member}->{$dir::UPDIR};
+}
+
+# Return whether this is an entry somewhere underneath the
+# specified directory.
+sub is_under {
+ my $dir = $_[0];
+ while ($dir) {
+ return 1 if $_[1] == $dir;
+ $dir = $dir->up;
+ }
+ return undef;
+}
+
+# Return the relative path from the calling directory ($_[1])
+# to the object. If the object is not under the directory, then
+# we return it as a top-relative or absolute path name.
+sub relpath {
+ my ($dir, $obj) = @_;
+ my @dirs;
+ my $o = $obj;
+ while ($o) {
+ if ($dir == $o) {
+ if (@dirs < 2) {
+ return $dirs[0] || '';
+ } else {
+ return File::Spec->catdir(@dirs);
+ }
+ }
+ unshift(@dirs, $o->{entry});
+ $o = $o->up;
+ }
+ # The object was not underneath the specified directory.
+ # Use the node's cached path, which is either top-relative
+ # (in which case we append '#' to the beginning) or
+ # absolute.
+ my $p = $obj->path;
+ $p = '#' . $p if ! File::Spec->file_name_is_absolute($p);
+ return $p;
+}
+
+# Return the path of the directory (file paths implemented
+# separately, below).
+sub path {
+ $_[0]->{path} ||
+ ($_[0]->{path} = $_[0]->{dir}->prefix . $_[0]->{entry});
+}
+
+# Return the pathname as a prefix to be concatenated with an entry.
+sub prefix {
+ return $_[0]->{prefix} if exists $_[0]->{prefix};
+ $_[0]->{prefix} = $_[0]->path . $SEPARATOR;
+}
+
+# Return the related source path prefix.
+sub srcprefix {
+ return $_[0]->{srcprefix} if exists $_[0]->{srcprefix};
+ my($srcdir) = $_[0]->srcdir;
+ $srcdir->{srcprefix} = $srcdir eq $_[0] ? $srcdir->prefix
+ : $srcdir->srcprefix;
+}
+
+# Return the related source directory.
+sub srcdir {
+ $_[0]->{'srcdir'} ||
+ ($_[0]->{'srcdir'} = $_[0]->{dir}->srcdir->lookupdir($_[0]->{entry}))
+}
+
+# Return if the directory is linked to a separate source directory.
+sub is_linked {
+ return $_[0]->{is_linked} if defined $_[0]->{is_linked};
+ $_[0]->{is_linked} = $_[0]->path ne $_[0]->srcdir->path;
+}
+
+sub link {
+ my(@paths) = @_;
+ my($srcdir) = $dir::cwd->lookupdir(pop @paths)->srcdir;
+ map($dir::cwd->lookupdir($_)->{'srcdir'} = $srcdir, @paths);
+
+ # make a reverse lookup for the link.
+ $srcdir->{links} = [] if ! $srcdir->{links};
+ push @{$srcdir->{links}}, @paths;
+}
+
+use vars qw( @tail ); # TODO: Why global ????
+
+sub linked_targets {
+ my $tgt = shift;
+ my @targets = ();
+ my $dir;
+ if (ref $tgt eq 'dir') {
+ $dir = $tgt;
+ } else {
+ push @tail, $tgt;
+ $dir = $tgt->{dir};
+ }
+ while ($dir) {
+ if (defined $dir->{links} && @{$dir->{links}}) {
+ push @targets,
+ map(File::Spec->catdir($_, @tail), @{$dir->{links}});
+ #print STDERR "Found Link: ${\$dir->path} -> @{\$dir->{links}}\n";
+ }
+ unshift @tail, $dir->{entry};
+ $dir = $dir->up;
+ }
+
+ return map($dir::top->lookupdir($_), @targets);
+}
+
+sub accessible {
+ my $path = $_[0]->path;
+ my $err = "$0: you have attempted to use path \"$path\" both as a file " .
+ "and as a directory!\n";
+ die $err;
+}
+
+sub init {
+ my $path = Cwd::cwd();
+
+ # We know we can get away with passing undef to lookupdir
+ # as the directory because $dir is an absolute path.
+ $top = lookupdir(undef, $path);
+ $top->{'path'} = $top->{srcpath} = $dir::CURDIR;
+ $top->{'prefix'} = '';
+ $top->{'srcdir'} = $top;
+
+ $cwd = $top;
+
+ ($cwd_vol, undef, undef) = File::Spec->splitpath($path);
+ $cwd_vol = '' if ! defined $cwd_vol;
+ $cwd_vol = uc($cwd_vol) if File::Spec->case_tolerant;
+}
+
+package file;
+
+use vars qw( @ISA $level );
+
+BEGIN { @ISA = qw(dir); $level = 0 }
+
+# Return the pathname of the file.
+# Define this separately from dir::path because we don't want to
+# cache all file pathnames (just directory pathnames).
+sub path {
+ $_[0]->{dir}->prefix . $_[0]->{entry}
+}
+
+# Return the related source file path.
+sub srcpath {
+ $_[0]->{dir}->srcprefix . $_[0]->{entry}
+}
+
+# Return if the file is (should be) linked to a separate source file.
+sub is_linked {
+ $_[0]->{dir}->is_linked
+}
+
+# Repository file search. If the local file exists, that wins.
+# Otherwise, return the first existing same-named file under a
+# Repository directory. If there isn't anything with the same name
+# under a Repository directory, return the local file name anyway
+# so that some higher layer can try to construct it.
+sub rfile {
+ return $_[0]->{rfile} if exists $_[0]->{rfile};
+ my($self) = @_;
+ my($rfile) = $self;
+ if (@param::rpath) {
+ my($path) = $self->path;
+ if (! File::Spec->file_name_is_absolute($path) && ! -f $path) {
+ my($dir);
+ foreach $dir (@param::rpath) {
+ my($t) = $dir->prefix . $path;
+ if (-f $t) {
+ $rfile = $_[0]->lookupfile($t);
+ $rfile->{'lfile'} = $self;
+ last;
+ }
+ }
+ }
+ }
+ $self->{rfile} = $rfile;
+}
+
+# Returns the local file for a repository file;
+# returns self if it's already a local file.
+sub lfile {
+ $_[0]->{'lfile'} || $_[0]
+}
+
+# returns the "precious" status of this file.
+sub precious {
+ return $_[0]->{precious};
+}
+
+# "Erase" reference to a Repository file,
+# making this a completely local file object
+# by pointing it back to itself.
+sub no_rfile {
+ $_[0]->{'rfile'} = $_[0];
+}
+
+# Return a path to the first existing file under a Repository directory,
+# implicitly returning the current file's path if there isn't a
+# same-named file under a Repository directory.
+sub rpath {
+ $_[0]->{rpath} ||
+ ($_[0]->{rpath} = $_[0]->rfile->path)
+}
+
+# Return a path to the first linked srcpath file under a Repositoy
+# directory, implicitly returning the current file's srcpath if there
+# isn't a same-named file under a Repository directory.
+sub rsrcpath {
+ return $_[0]->{rsrcpath} if exists $_[0]->{rsrcpath};
+ my($self) = @_;
+ my($path) = $self->{rsrcpath} = $self->srcpath;
+ if (@param::rpath && ! File::Spec->file_name_is_absolute($path) && ! -f $path) {
+ my($dir);
+ foreach $dir (@param::rpath) {
+ my($t) = $dir->prefix . $path;
+ if (-f $t) {
+ $self->{rsrcpath} = $t;
+ last;
+ }
+ }
+ }
+ $self->{rsrcpath};
+}
+
+# Return if a same-named file source file exists.
+# This handles the interaction of Link and Repository logic.
+# As a side effect, it will link a source file from its Linked
+# directory (preferably local, but maybe in a repository)
+# into a build directory from its proper Linked directory.
+sub source_exists {
+ return $_[0]->{source_exists} if defined $_[0]->{source_exists};
+ my($self) = @_;
+ my($path) = $self->path;
+ my($mtime, $ctime) = (stat($path))[9,10];
+ if ($self->is_linked) {
+ # Linked directory, local logic.
+ my($srcpath) = $self->srcpath;
+ my($src_mtime, $src_ctime) = (stat($srcpath))[9,10];
+ if ($src_mtime) {
+ if (! $mtime || $src_mtime != $mtime || $src_ctime != $ctime) {
+ futil::install($srcpath, $self);
+ }
+ return $self->{source_exists} = 1;
+ }
+ # Linked directory, repository logic.
+ if (@param::rpath) {
+ if ($self != $self->rfile) {
+ return $self->{source_exists} = 1;
+ }
+ my($rsrcpath) = $self->rsrcpath;
+ if ($path ne $rsrcpath) {
+ my($rsrc_mtime, $rsrc_ctime) = (stat($rsrcpath))[9,10];
+ if ($rsrc_mtime) {
+ if (! $mtime || $rsrc_mtime != $mtime
+ || $rsrc_ctime != $ctime) {
+ futil::install($rsrcpath, $self);
+ }
+ return $self->{source_exists} = 1;
+ }
+ }
+ }
+ # There was no source file in any Linked directory
+ # under any Repository. If there's one in the local
+ # build directory, it no longer belongs there.
+ if ($mtime) {
+ unlink($path) || die("$0: couldn't unlink $path ($!)\n");
+ }
+ return $self->{source_exists} = '';
+ } else {
+ if ($mtime) {
+ return $self->{source_exists} = 1;
+ }
+ if (@param::rpath && $self != $self->rfile) {
+ return $self->{source_exists} = 1;
+ }
+ return $self->{source_exists} = '';
+ }
+}
+
+# Return if a same-named derived file exists under a Repository directory.
+sub derived_exists {
+ $_[0]->{derived_exists} ||
+ ($_[0]->{derived_exists} = ($_[0] != $_[0]->rfile));
+}
+
+# Return if this file is somewhere under a Repository directory.
+sub is_on_rpath {
+ defined $_[0]->{'lfile'};
+}
+
+sub local {
+ my($self, $arg) = @_;
+ if (defined $arg) {
+ $self->{'local'} = $arg;
+ }
+ $self->{'local'};
+}
+
+# Return the entry name of the specified file with the specified
+# suffix appended. Leave it untouched if the suffix is already there.
+# Differs from the addsuffix function, below, in that this strips
+# the existing suffix (if any) before appending the desired one.
+sub base_suf {
+ my($entry) = $_[0]->{entry};
+ if ($entry !~ m/$_[1]$/) {
+ $entry =~ s/\.[^\.]*$//;
+ $entry .= $_[1];
+ }
+ $entry;
+}
+
+# Return the suffix of the file; everything including and to the
+# right of the last dot.
+sub suffix {
+ my @pieces = split(/\./, $_[0]->{entry});
+ my $suffix = pop(@pieces);
+ return ".$suffix";
+}
+
+# Called as a simple function file::addsuffix(name, suffix)
+sub addsuffix {
+ my($name, $suffix) = @_;
+
+ if ($suffix && substr($name, -length($suffix)) ne $suffix) {
+ return $name .= $suffix;
+ }
+ $name;
+}
+
+# Return true if the file is (or will be) accessible.
+# That is, if we can build it, or if it is already present.
+sub accessible {
+ (exists $_[0]->{builder}) || ($_[0]->source_exists);
+}
+
+# Return true if the file should be ignored for the purpose
+# of computing dependency information (should not be considered
+# as a dependency and, further, should not be scanned for
+# dependencies).
+sub ignore {
+ return 0 if !$param::ignore;
+ return $_[0]->{ignore} if exists $_[0]->{ignore};
+ $_[0]->{ignore} = $_[0]->path =~ /$param::ignore/o;
+}
+
+# Build the file, if necessary.
+sub build {
+ return $_[0]->{status} if $_[0]->{status};
+ my($status) = &file::_build;
+ if ($_[0]->{after_build_func}) {
+ my($pkgvars) = $_[0]->{conscript}->{pkgvars};
+ NameSpace::restore('script', $pkgvars) if $pkgvars;
+ eval("package script; " . $_[0]->{after_build_func});
+ print "Error running AfterBuild for ${\$_[0]->path}: $@\n" if ($@);
+ NameSpace::remove('script', keys %$pkgvars) if $pkgvars;
+ }
+ return $status;
+}
+
+sub _build {
+ my($self) = @_;
+ print main::DEPFILE $self->path, "\n" if $param::depfile;
+ print((' ' x $level), "Checking ", $self->path, "\n") if $param::depends;
+ if (!exists $self->{builder}) {
+ # We don't know how to build the file. This is OK, if
+ # the file is present as a source file, under either the
+ # local tree or a Repository.
+ if ($self->source_exists) {
+ return $self->{status} = 'handled';
+ } else {
+ my($name) = $self->path;
+ print("$0: don't know how to construct \"$name\"\n");
+ exit(1) unless $param::kflag;
+ return $self->{status} = 'errors'; # xxx used to be 'unknown'
+ }
+ }
+
+ # An associated build object exists, so we know how to build
+ # the file. We first compute the signature of the file, based
+ # on its dependendencies, then only rebuild the file if the
+ # signature has changed.
+ my($builder) = $self->{builder};
+ $level += 2;
+
+ my(@deps) = (@{$self->{dep}}, @{$self->{sources}});
+ my($rdeps) = \@deps;
+
+ if ($param::random) {
+ # If requested, build in a random order, instead of the
+ # order that the dependencies were listed.
+ my(%rdeps);
+ map { $rdeps{$_,'*' x int(rand 10)} = $_ } @deps;
+ $rdeps = [values(%rdeps)];
+ }
+
+ $self->{status} = '';
+
+ my $dep;
+ for $dep (@$rdeps) {
+ if ((build $dep) eq 'errors') {
+ # Propagate dependent errors to target.
+ # but try to build all dependents regardless of errors.
+ $self->{status} = 'errors';
+ }
+ }
+
+ # If any dependents had errors, then we abort.
+ if ($self->{status} eq 'errors') {
+ $level -= 2;
+ return 'errors';
+ }
+
+ # Compute the final signature of the file, based on
+ # the static dependencies (in order), dynamic dependencies,
+ # output path name, and (non-substituted) build script.
+ my($sig) = 'sig'->collect(map('sig'->signature($_->rfile), @deps),
+ $builder->includes($self),
+ $builder->scriptsig);
+
+ # May have gotten errors during computation of dynamic
+ # dependency signature, above.
+ $level -= 2;
+ return 'errors' if $self->{status} eq 'errors';
+
+ if (@param::rpath && $self->derived_exists) {
+ # There is no local file of this name, but there is one
+ # under a Repository directory.
+
+ if ('sig'->current($self->rfile, $sig)) {
+ # The Repository copy is current (its signature matches
+ # our calculated signature).
+ if ($self->local) {
+ # ...but they want a local copy, so provide it.
+ main::showcom("Local copy of ${\$self->path} from " .
+ "${\$self->rpath}");
+ futil::install($self->rpath, $self);
+ 'sig'->bsig($self, $sig);
+ }
+ return $self->{status} = 'handled';
+ }
+
+ # The signatures don't match, implicitly because something
+ # on which we depend exists locally. Get rid of the reference
+ # to the Repository file; we'll build this (and anything that
+ # depends on it) locally.
+ $self->no_rfile;
+ }
+
+ # Then check for currency.
+ if (! 'sig'->current($self, $sig)) {
+ # We have to build/derive the file.
+ print((' ' x $level), "Rebuilding ", $self->path, ": out of date.\n")
+ if $param::depends;
+ # First check to see if the built file is cached.
+ if ($builder->cachin($self, $sig)) {
+ 'sig'->bsig($self, $sig);
+ return $self->{status} = 'built';
+ } elsif ($builder->action($self)) {
+ $builder->cachout($self, $sig);
+ 'sig'->bsig($self, $sig);
+ return $self->{status} = 'built';
+ } else {
+ die("$0: errors constructing ${\$self->path}\n")
+ unless $param::kflag;
+ return $self->{status} = 'errors';
+ }
+ } else {
+ # Push this out to the cache if we've been asked to (-C option).
+ # Don't normally do this because it slows us down.
+ # In a fully built system, no accesses to the cache directory
+ # are required to check any files. This is a win if cache is
+ # heavily shared. Enabling this option puts the directory in the
+ # loop. Useful only when you wish to recreate a cache from a build.
+ if ($param::cachesync) {
+ $builder->cachout($self, $sig);
+ 'sig'->bsig($self, $sig);
+ }
+ return $self->{status} = 'handled';
+ }
+}
+
+# Bind an action to a file, with the specified sources. No return value.
+sub bind {
+ my($self, $builder, @sources) = @_;
+ if ($self->{builder} && !$self->{builder}->compatible($builder)) {
+ # Even if not "compatible", we can still check to see if the
+ # derivation is identical. It should be identical if the builder is
+ # the same and the sources are the same.
+ if ("$self->{builder} @{$self->{sources}}" ne "$builder @sources") {
+ $main::errors++;
+ my($_foo1, $script1, $line1, $sub1) = @{$self->creator};
+ my($_foo2, $script2, $line2, $sub2) = script::caller_info;
+ my $err = "\t${\$self->path}\n" .
+ "\tbuilt (at least) two different ways:\n" .
+ "\t\t$script1, line $line1: $sub1\n" .
+ "\t\t$script2, line $line2: $sub2\n";
+ die $err;
+ }
+ return;
+ }
+ if ($param::wflag) {
+ my($script, $line, $sub);
+ (undef, $script, $line, $sub) = script::caller_info;
+ $self->{script} = '' if ! defined $self->{script};
+ $self->{script} .= "; " if $self->{script};
+ $self->{script} .= qq($sub in "$script", line $line);
+ }
+ $self->{builder} = $builder;
+ push(@{$self->{sources}}, @sources);
+ @{$self->{dep}} = () if ! defined $self->{dep};
+ $self->{conscript} = $priv::self->{script};
+}
+
+sub is_under {
+ $_[0]->{dir}->is_under($_[1]);
+}
+
+sub relpath {
+ my $dirpath = $_[0]->relpath($_[1]->{dir});
+ if (! $dirpath) {
+ return $_[1]->{entry};
+ } else {
+ File::Spec->catfile($dirpath, $_[1]->{entry});
+ }
+}
+
+# Return the signature array for this file.
+# This probably belongs in its own "sigarray" package,
+# which would make it easier to optimize performance.
+sub sigarray {
+ if ($_[0]->{sigaref}) {
+ return @{$_[0]->{sigaref}};
+ }
+ my $self = shift;
+ # glob2pat based on The Perl Cookbook, p. 180.
+ sub glob2pat {
+ my $globstr = shift;
+ my %patmap = (
+ '*' => '.*',
+ '?' => '.',
+ '[' => '[',
+ ']' => ']',
+ '/' => "\Q$dir::SEPARATOR", # Cons-specific modification
+ );
+ $globstr =~ s{(.)} { $patmap{$1} || "\Q$1" }ge;
+ return '^' . $globstr . '$';
+ }
+ my @sigarray;
+ my $default;
+ my $builder = $self->lfile->{builder};
+ if (! $builder) {
+ @sigarray = @$param::sourcesig;
+ $default = [qw(content)];
+ } else {
+ if ($builder->{env} && $builder->{env}->{SIGNATURE}) {
+ @sigarray = @{$builder->{env}->{SIGNATURE}};
+ } else {
+ my $class = ref $builder;
+ my $path = $self->path;
+ warn qq($0: Warning: Builder package $class did not record\n) .
+ qq(\tthe calling environment for '$path'.\n) .
+ qq(\tUnable to use any %SIGNATURE construction variable\n) .
+ qq(\tfor signature configuration.\n);
+ }
+ $default = [qw(build)];
+ }
+ my $path = $self->path;
+ while (@sigarray) {
+ my($glob, $aref) = splice(@sigarray, 0, 2);
+ my $re = glob2pat($glob);
+ if ($path =~ /$re/) {
+ $aref = [split(/\s+/, $aref)] if ! ref $aref;
+ $self->{sigaref} = $aref;
+ return @$aref;
+ }
+ }
+ $self->{sigaref} = $default;
+ return @{$self->{sigaref}}
+}
+
+# Decide if this file's signature should be the content or build signature.
+sub sigtype {
+ if ($_[0]->{sigtype}) {
+ return $_[0]->{sigtype};
+ }
+ my $self = shift;
+ my @sigarray = $self->sigarray;
+ my $sigtype;
+ if (grep($_ eq "build", @sigarray)) {
+ $sigtype = 'bsig';
+ } elsif (grep($_ =~ /content$/, @sigarray)) {
+ $sigtype = 'csig';
+ }
+ return $self->{sigtype} = $sigtype;
+}
+
+# Return whether this file is configured to use stored
+# signature values from the .consign file.
+sub stored {
+ if (! defined $_[0]->{stored}) {
+ $_[0]->{stored} = grep($_ eq "stored-content", $_[0]->sigarray);
+ }
+ return $_[0]->{stored};
+}
+
+# Generic entry (file or directory) handling.
+# This is an empty subclass for nodes that haven't
+# quite decided whether they're files or dirs.
+# Use file methods until someone blesses them one way or the other.
+package entry;
+
+use vars qw( @ISA );
+
+BEGIN { @ISA = qw(file) }
+
+# File utilities
+package futil;
+
+# Install one file as another.
+# Links them if possible (hard link), otherwise copies.
+# Don't ask why, but the source is a path, the tgt is a file obj.
+sub install {
+ my($sp, $tgt) = @_;
+ my($tp) = $tgt->path;
+ return 1 if $tp eq $sp;
+ return 1 if eval { link($sp, $tp) };
+ unlink($tp);
+ if (! futil::mkdir($tgt->{dir})) {
+ return undef;
+ }
+ return 1 if eval { link($sp, $tp) };
+ futil::copy($sp, $tp);
+}
+
+# Copy one file to another. Arguments are actual file names.
+# Returns undef on failure. Preserves mtime and mode.
+sub copy {
+ my ($sp, $tp) = @_;
+ my ($mode, $length, $atime, $mtime) = (stat($sp))[2,7,8,9];
+
+ # Use Perl standard library module for file copying, which handles
+ # binary copies. <schwarze@isa.de> 1998-06-18
+ if (! File::Copy::copy($sp, $tp)) {
+ warn qq($0: can\'t install "$sp" to "$tp" ($!)\n); #'
+ return undef;
+ }
+ # The file has been created, so try both the chmod and utime,
+ # first making sure the copy is writable (because permissions
+ # affect the ability to modify file times on some operating
+ # systems), and then changing permissions back if necessary.
+ my $ret = 1;
+ my $wmode = $mode | 0700;
+ if (! chmod $wmode, $tp) {
+ warn qq($0: can\'t set mode $wmode on file "$tp" ($!)\n); #'
+ $ret = undef;
+ }
+ if (! utime $atime, $mtime, $tp) {
+ warn qq($0: can\'t set modification time for file "$tp" ($!)\n); #'
+ $ret = undef;
+ }
+ if ($mode != $wmode && ! chmod $mode, $tp) {
+ warn qq($0: can\'t set mode $mode on file "$tp" ($!)\n); #'
+ $ret = undef;
+ }
+ return $ret;
+}
+
+# Ensure that the specified directory exists.
+# Aborts on failure.
+sub mkdir {
+ return 1 if $_[0]->{'exists'};
+ if (! futil::mkdir($_[0]->{dir})) { # Recursively make parent.
+ return undef;
+ }
+ my($path) = $_[0]->path;
+ if (!-d $path && !mkdir($path, 0777)) {
+ warn qq($0: can't create directory $path ($!).\n); #'
+ return undef;
+ }
+ $_[0]->{'exists'} = 1;
+}
+
+
+# Signature package.
+package sig::hash;
+
+use vars qw( $called );
+
+sub init {
+ my($dir) = @_;
+ my($consign) = $dir->prefix . ".consign";
+ my($dhash) = $dir->{consign} = {};
+ if (-f $consign) {
+ open(CONSIGN, $consign) || die("$0: can't open $consign ($!)\n");
+ while(<CONSIGN>) {
+ chop;
+ my ($file, $sig) = split(/:/,$_);
+ $dhash->{$file} = $sig;
+ }
+ close(CONSIGN);
+ }
+ $dhash
+}
+
+# Read the hash entry for a particular file.
+sub in {
+ my($dir) = $_[0]->{dir};
+ ($dir->{consign} || init($dir))->{$_[0]->{entry}}
+}
+
+# Write the hash entry for a particular file.
+sub out {
+ my($file, $sig) = @_;
+ my($dir) = $file->{dir};
+ ($dir->{consign} || init($dir))->{$file->{entry}} = $sig;
+ $sig::hash::dirty{$dir} = $dir;
+}
+
+# Eliminate the hash entry for a particular file.
+sub clear {
+ my($file) = @_;
+ my($dir) = $file->{dir};
+ delete $dir->{consign}->{$file->{entry}} if $dir->{consign};
+ $sig::hash::dirty{$dir} = $dir;
+}
+
+# Flush hash entries. Called at end or via ^C interrupt.
+sub END {
+ return if $called++; # May be called twice.
+ close(CONSIGN); # in case this came in via ^C.
+ my $dir;
+ for $dir (values %sig::hash::dirty) {
+ my($consign) = $dir->prefix . ".consign";
+ my($constemp) = $consign . ".$$";
+ if (! open(CONSIGN, ">$constemp")) {
+ die("$0: can't create $constemp ($!)\n");
+ }
+ my($entry, $sig);
+ while (($entry, $sig) = each %{$dir->{consign}}) {
+ if (! print CONSIGN "$entry:$sig\n") {
+ die("$0: error writing to $constemp ($!)\n");
+ }
+ }
+ close(CONSIGN);
+ if (! rename($constemp, $consign)) {
+ if (futil::copy($constemp, $consign)) {
+ unlink($constemp);
+ } else {
+ die("$0: couldn't rename or copy $constemp to $consign " .
+ "($!)\n");
+ }
+ }
+ }
+}
+
+
+# Derived file caching.
+package cache;
+
+# Find a file in the cache. Return non-null if the file is in the cache.
+sub in {
+ return undef unless $param::cache;
+ my($file, $sig) = @_;
+ # Add the path to the signature, to make it unique.
+ $sig = 'sig'->collect($sig, $file->path) unless $param::mixtargets;
+ my($dir) = substr($sig, 0, 1);
+ my($cp) = File::Spec->catfile($param::cache, $dir, $sig);
+ return -f $cp && futil::install($cp, $file);
+}
+
+# Try to flush a file to the cache, if not already there.
+# If it doesn't make it out, due to an error, then that doesn't
+# really matter.
+sub out {
+ return unless $param::cache;
+ my($file, $sig) = @_;
+ # Add the path to the signature, to make it unique.
+ $sig = 'sig'->collect($sig, $file->path) unless $param::mixtargets;
+ my($dir) = substr($sig, 0, 1);
+ my($sp) = $file->path;
+ my($cp) = File::Spec->catfile($param::cache, $dir, $sig);
+ my($cdir) = File::Spec->catfile($param::cache, $dir);
+ if (! -d $cdir) {
+ mkdir($cdir, 0777) ||
+ die("$0: can't create cache directory $cdir ($!).\n");
+ } elsif (-f $cp) {
+ # Already cached: try to use that instead, to save space.
+ # This can happen if the -cs option is used on a previously
+ # uncached build, or if two builds occur simultaneously.
+ my($lp) = ".$sig";
+ unlink($lp);
+ return if ! eval { link($cp, $lp) };
+ rename($lp, $sp);
+ # Unix98 says, "If the old argument and the new argument both
+ # [refer] to the same existing file, the rename() function
+ # returns successfully and performs no other action." So, if
+ # $lp and $sp are links (i.e., $cp and $sp are links), $lp is
+ # left, and we must unlink it ourselves. If the rename failed
+ # for any reason, it is also good form to unlink the temporary
+ # $lp. Otherwise $lp no longer exists and, barring some race,
+ # the unlink fails silently.
+ unlink($lp);
+ return;
+ }
+
+ return if eval { link($sp, $cp) };
+ return if ! -f $sp; # if nothing to cache.
+ if (futil::copy($sp, "$cp.new")) {
+ rename("$cp.new", $cp);
+ }
+}
+
+
+# Generic signature handling package.
+# This handles the higher-layer distinction between content and build
+# signatures, relying on an underlying calculation package like
+# "sig::md5"" to provide the signature values themselves.
+package sig;
+
+use vars qw( @ISA );
+
+# Select the underlying package to be used for signature calculation.
+# We play a few namespace games here. Specifically, we append
+# "sig::" to the beginning of the subclass we're passed. Then,
+# if the package ends in "::debug", we actually subclass the
+# "sig::debug" package and as a wrapper around the underlying
+# (e.g.) "sig::md5" package that's doing the real calculation.
+sub select {
+ my($package, $subclass) = @_;
+ my $p = $package . "::" . $subclass;
+ my $sigpkg = $p;
+ if ($p =~ /(.*)::debug$/) {
+ $sigpkg = $1;
+ $p = 'sig::debug';
+ }
+ @ISA = ($p);
+ $p->init($sigpkg);
+};
+
+# Set or return the build signature of a file.
+# This is computed elsewhere and passed in to us.
+sub bsig {
+ my($self, $file, $sig) = @_;
+ if (defined $sig) {
+ $file->{'bsig'} = $sig;
+ $self->set($file);
+ } elsif (! defined $file->{'bsig'}) {
+ $file->{'bsig'} = '';
+ }
+ $file->{'bsig'}
+}
+
+# Determine the content signature of a file.
+# This also sets the .consign entry unless the file is in a
+# repository; we don't write into repositories, only read from them.
+sub csig {
+ my($self, $file) = @_;
+ if (! $file->{'csig'}) {
+ $file->{'csig'} = $self->srcsig($file->path);
+ $self->set($file) if ! $file->is_on_rpath;
+ }
+ $_[1]->{'csig'}
+}
+
+# Determine the current signature of an already-existing or
+# non-existant file. Unless a specific signature type (bsig
+# or csig) is requested, this consults the file's signature
+# array to decide whether to return content or build signature,
+# and whether to use a cached value from a .consign file.
+sub signature {
+ my($self, $file, $sigtype) = @_;
+ $sigtype = $file->sigtype if ! $sigtype;
+ #open(TTY, ">/dev/tty");
+ #print TTY $file->path, ": $sigtype\n";
+ #close(TTY);
+ my($path) = $file->path;
+ my($time) = (stat($path))[9];
+ if ($time) {
+ if ($file->{$sigtype}) {
+ return $file->{$sigtype};
+ }
+ if ($file->is_on_rpath || $file->stored) {
+ if ('sig'->fetch($file) && $file->{$sigtype}) {
+ if ($file->{'sigtime'} == $time ||
+ ! $param::rep_sig_times_ok
+ && $file->is_on_rpath) {
+ return $file->{$sigtype};
+ }
+ }
+ $file->{$sigtype} = undef;
+ }
+ if ($file->is_on_rpath || ! File::Spec->file_name_is_absolute($path)) {
+ my $sig = '';
+ if ($sigtype eq 'bsig') { $sig = $self->bsig($file); }
+ elsif ($sigtype eq 'csig') { $sig = $self->csig($file); }
+ return $sig;
+ }
+ # This file is not in a repository or under the local directory
+ # structure. In the canonical case, it's a utility that will be
+ # executed by a command. Historically, Cons has returned the
+ # name of the command concatenated with the modification time.
+ # Note that this is *not* the path ("cc" not "/bin/cc"), so it
+ # would lose in the unlikely event that a different copy of the
+ # utility was used that happened to have the same modification
+ # time (due to living in a different directory on the PATH, for
+ # example). The obvious "fix" of using the path like so, however:
+ # return $path . $time;
+ # is wrong. In a multi-machine build environment, different
+ # systems may have the same utility in different locations (due
+ # to different NFS mount points, for example), which would
+ # cause a lot of unnecessary builds if we used the full path.
+ # A better solution to strengthen this signature would be to
+ # also concatenate the size of the file, but that would cause
+ # unnecessary rebuilds when coming from .consign files that used
+ # the old scheme. All of which is to merely explain why we're
+ # leaving this as it has been, but documenting it here in case
+ # there's reason to change it in the future.
+ return $file->{entry} . $time;
+ }
+ return $file->{$sigtype} = '';
+}
+
+sub bsignature {
+ my($self, $file) = @_;
+ my($path) = $file->path;
+ my($time) = (stat($path))[9];
+ if ($time) {
+ if ($file->{'bsig'}) {
+ return $file->{'bsig'};
+ }
+ if ('sig'->fetch($file, 'bsig') && $file->{'bsig'}) {
+ if ($file->{'sigtime'} == $time ||
+ ! $param::rep_sig_times_ok
+ && $file->is_on_rpath) {
+ return $file->{'bsig'};
+ }
+ }
+ if ($file->is_on_rpath || ! File::Spec->file_name_is_absolute($path)) {
+ return $self->bsig($file);
+ }
+ return $path . $time;
+ }
+ return $file->{'bsig'} = '';
+}
+
+# Invalidate a file's signature, also clearing its .consign entry.
+sub invalidate {
+ my($self, $file) = @_;
+ delete $file->{'sigtime'};
+ delete $file->{'bsig'};
+ delete $file->{'csig'};
+ sig::hash::clear($file);
+}
+
+# Store the signature for a file.
+sub set {
+ my($self, $file) = @_;
+ my $sig = (stat($file->path))[9];
+ $sig .= " " . ($file->{'bsig'} || '-');
+ $sig .= " " . $file->{'csig'} if $file->{'csig'};
+ sig::hash::out($file, $sig);
+}
+
+# Fetch the signature(s) for a file.
+# Returns whether there was a signature to fetch.
+sub fetch {
+ my($self, $file, @kw) = @_;
+ @kw = ('bsig', 'csig') if ! @kw;
+ my $sig = sig::hash::in($file) || '';
+ my($sigtime, $bsig, $csig) = split(/ /, $sig);
+ $file->{'sigtime'} = $sigtime;
+ $file->{'bsig'} = $bsig || '' if grep($_ eq 'bsig', @kw);
+ $file->{'csig'} = $csig || '' if grep($_ eq 'csig', @kw);
+ $file->{'bsig'} = '' if $file->{'bsig'} eq '-';
+ return $sig ne '';
+}
+
+# MD5-based signature package.
+package sig::md5;
+
+use vars qw( $md5 );
+
+# Initialize MD5 signature calculation by finding an appropriate
+# module and creating the proper object.
+sub init {
+ my $self = shift;
+ my @md5_modules = qw(Digest::MD5 MD5 Digest::Perl::MD5);
+ # We used to find the right module more simply, using $_ as the
+ # loop iterator and just doing:
+ #
+ # eval "use $_";
+ # $module = $_, $last if ! $@;
+ #
+ # in the loop. Empirically, though, this doesn't pass back the
+ # right value in $module on some ActiveState versions. (Maybe
+ # it's something to do with the eval in a for loop, I dunno.)
+ # Work around it by using $_ to pass the value out of the loop,
+ # which seems to work everywhere.
+ my $module;
+ for $module (@md5_modules) {
+ eval "use $module";
+ $_ = $module, last if ! $@;
+ }
+ $module = $_;
+ die "Cannot find any MD5 module from: @md5_modules" if $@;
+
+ $md5 = new $module;
+}
+
+# Is the provided signature equal to the signature of the current
+# instantiation of the target (and does the target exist)?
+sub current {
+ my($self, $file, $sig, $sigtype) = @_;
+ $self->bsignature($file) eq $sig;
+}
+
+# Return an aggregate signature for a list of signature values.
+sub collect {
+ my($self, @sigs) = @_;
+ # The following sequence is faster than calling the hex interface.
+ $md5->reset();
+ $md5->add(join('', $param::salt, @sigs));
+ unpack("H*", $md5->digest());
+}
+
+# Directly compute a file signature as the MD5 checksum of the
+# bytes in the file.
+sub srcsig {
+ my($self, $path) = @_;
+ $md5->reset();
+ open(FILE, $path) || return '';
+ binmode(FILE);
+ $md5->addfile(\*FILE);
+ close(FILE);
+ unpack("H*", $md5->digest());
+}
+
+# Compute the signature of a command string.
+# For MD5, this is just the string itself, since MD5 will condense
+# the string contents into the ultimate signature. Other signature
+# schemes may need to figure this out differently.
+sub cmdsig {
+ my($self, $sig) = @_;
+ return $sig
+}
+
+# Generic debug package for signature calculation.
+# Because of the way we're called by sig::select() and then use
+# the specified value to set up @ISA, this package is essentially a
+# factory that creates packages like sig::md5::debug, etc., on the fly.
+package sig::debug;
+
+use vars qw( @ISA $sigpkg $outfh );
+
+local *FH;
+
+sub init {
+ my $self = shift;
+ $sigpkg = shift;
+ @ISA = ($sigpkg);
+ $sigpkg->init();
+ my $file = $ENV{CONS_SIG_DEBUG};
+ if ($file) {
+ if (! open(FH, ">$file")) {
+ die "Cannot open $file: $!";
+ }
+ $outfh = \*FH;
+ } else {
+ $outfh = \*STDOUT;
+ }
+}
+
+sub current {
+ my($self, $file, $sig, $sigtype) = @_;
+ my $fsig = $self->bsignature($file);
+ my $sub = "${sigpkg}::current";
+ my $sep = "\n" . ' ' x (length($sub) + 1 - 3);
+ print $outfh "$sub(|$fsig|${sep}eq |$sig|)\n";
+ return $fsig eq $sig;
+}
+
+sub collect {
+ my($self, @sigs) = @_;
+ my $sig = $sigpkg->collect(@sigs);
+ my $sub = "${sigpkg}::collect";
+ my $sep = ",\n" . ' ' x (length($sub) + 1);
+ my $buf = join($sep, @sigs);
+ $buf = $param::salt . $sep . $buf if $param::salt;
+ print $outfh "$sub($buf)\n\t=> |$sig|\n";
+ return $sig;
+}
+
+sub srcsig {
+ my($self, $path) = @_;
+ my $sig = $sigpkg->srcsig($path);
+ print $outfh "${sigpkg}::srcsig($path)\n\t=> |$sig|\n";
+ return $sig;
+}
+
+__END__;
+
+=head1 NAME
+
+Cons - A Software Construction System
+
+=head1 DESCRIPTION
+
+A guide and reference for version 2.3.1
+
+Copyright (c) 1996-2001 Free Software Foundation, Inc.
+
+This program is free software; you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation; either version 2 of the License, or
+(at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; see the file COPYING. If not, write to
+the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+Boston, MA 02111-1307, USA.
+
+=head1 Introduction
+
+B<Cons> is a system for constructing, primarily, software, but is quite
+different from previous software construction systems. Cons was designed
+from the ground up to deal easily with the construction of software spread
+over multiple source directories. Cons makes it easy to create build scripts
+that are simple, understandable and maintainable. Cons ensures that complex
+software is easily and accurately reproducible.
+
+Cons uses a number of techniques to accomplish all of this. Construction
+scripts are just Perl scripts, making them both easy to comprehend and very
+flexible. Global scoping of variables is replaced with an import/export
+mechanism for sharing information between scripts, significantly improving
+the readability and maintainability of each script. B<Construction
+environments> are introduced: these are Perl objects that capture the
+information required for controlling the build process. Multiple
+environments are used when different semantics are required for generating
+products in the build tree. Cons implements automatic dependency analysis
+and uses this to globally sequence the entire build. Variant builds are
+easily produced from a single source tree. Intelligent build subsetting is
+possible, when working on localized changes. Overrides can be setup to
+easily override build instructions without modifying any scripts. MD5
+cryptographic B<signatures> are associated with derived files, and are used
+to accurately determine whether a given file needs to be rebuilt.
+
+While offering all of the above, and more, Cons remains simple and easy to
+use. This will, hopefully, become clear as you read the remainder of this
+document.
+
+
+=head1 Why Cons? Why not Make?
+
+Cons is a B<make> replacement. In the following paragraphs, we look at a few
+of the undesirable characteristics of make--and typical build environments
+based on make--that motivated the development of Cons.
+
+=head2 Build complexity
+
+Traditional make-based systems of any size tend to become quite complex. The
+original make utility and its derivatives have contributed to this tendency
+in a number of ways. Make is not good at dealing with systems that are
+spread over multiple directories. Various work-arounds are used to overcome
+this difficulty; the usual choice is for make to invoke itself recursively
+for each sub-directory of a build. This leads to complicated code, in which
+it is often unclear how a variable is set, or what effect the setting of a
+variable will have on the build as a whole. The make scripting language has
+gradually been extended to provide more possibilities, but these have
+largely served to clutter an already overextended language. Often, builds
+are done in multiple passes in order to provide appropriate products from
+one directory to another directory. This represents a further increase in
+build complexity.
+
+
+=head2 Build reproducibility
+
+The bane of all makes has always been the correct handling of
+dependencies. Most often, an attempt is made to do a reasonable job of
+dependencies within a single directory, but no serious attempt is made to do
+the job between directories. Even when dependencies are working correctly,
+make's reliance on a simple time stamp comparison to determine whether a
+file is out of date with respect to its dependents is not, in general,
+adequate for determining when a file should be rederived. If an external
+library, for example, is rebuilt and then ``snapped'' into place, the
+timestamps on its newly created files may well be earlier than the last
+local build, since it was built before it became visible.
+
+
+=head2 Variant builds
+
+Make provides only limited facilities for handling variant builds. With the
+proliferation of hardware platforms and the need for debuggable
+vs. optimized code, the ability to easily create these variants is
+essential. More importantly, if variants are created, it is important to
+either be able to separate the variants or to be able to reproduce the
+original or variant at will. With make it is very difficult to separate the
+builds into multiple build directories, separate from the source. And if
+this technique isn't used, it's also virtually impossible to guarantee at
+any given time which variant is present in the tree, without resorting to a
+complete rebuild.
+
+
+=head2 Repositories
+
+Make provides only limited support for building software from code that
+exists in a central repository directory structure. The VPATH feature of
+GNU make (and some other make implementations) is intended to provide this,
+but doesn't work as expected: it changes the path of target file to the
+VPATH name too early in its analysis, and therefore searches for all
+dependencies in the VPATH directory. To ensure correct development builds,
+it is important to be able to create a file in a local build directory and
+have any files in a code repository (a VPATH directory, in make terms) that
+depend on the local file get rebuilt properly. This isn't possible with
+VPATH, without coding a lot of complex repository knowledge directly into
+the makefiles.
+
+
+=head1 Keeping it simple
+
+A few of the difficulties with make have been cited above. In this and
+subsequent sections, we shall introduce Cons and show how these issues are
+addressed.
+
+=head2 Perl scripts
+
+Cons is Perl-based. That is, Cons scripts--F<Conscript> and F<Construct>
+files, the equivalent to F<Makefile> or F<makefile>--are all written in
+Perl. This provides an immediate benefit: the language for writing scripts
+is a familiar one. Even if you don't happen to be a Perl programmer, it
+helps to know that Perl is basically just a simple declarative language,
+with a well-defined flow of control, and familiar semantics. It has
+variables that behave basically the way you would expect them to,
+subroutines, flow of control, and so on. There is no special syntax
+introduced for Cons. The use of Perl as a scripting language simplifies
+the task of expressing the appropriate solution to the often complex
+requirements of a build.
+
+
+=head2 Hello, World!
+
+To ground the following discussion, here's how you could build the B<Hello,
+World!> C application with Cons:
+
+
+
+ $env = new cons();
+ Program $env 'hello', 'hello.c';
+
+If you install this script in a directory, naming the script F<Construct>,
+and create the F<hello.c> source file in the same directory, then you can
+type C<cons hello> to build the application:
+
+
+
+ % cons hello
+ cc -c hello.c -o hello.o
+ cc -o hello hello.o
+
+
+=head2 Construction environments
+
+A key simplification of Cons is the idea of a B<construction environment>. A
+construction environment is an B<object> characterized by a set of key/value
+pairs and a set of B<methods>. In order to tell Cons how to build something,
+you invoke the appropriate method via an appropriate construction
+environment. Consider the following example:
+
+
+
+ $env = new cons(
+ CC => 'gcc',
+ LIBS => 'libworld.a'
+ );
+
+ Program $env 'hello', 'hello.c';
+
+In this case, rather than using the default construction environment, as is,
+we have overridden the value of C<CC> so that the GNU C Compiler equivalent
+is used, instead. Since this version of B<Hello, World!> requires a library,
+F<libworld.a>, we have specified that any program linked in this environment
+should be linked with that library. If the library exists already, well and
+good, but if not, then we'll also have to include the statement:
+
+
+
+ Library $env 'libworld', 'world.c';
+
+Now if you type C<cons hello>, the library will be built before the program
+is linked, and, of course, C<gcc> will be used to compile both modules:
+
+
+
+ % cons hello
+ gcc -c hello.c -o hello.o
+ gcc -c world.c -o world.o
+ ar r libworld.a world.o
+ ar: creating libworld.a
+ ranlib libworld.a
+ gcc -o hello hello.o libworld.a
+
+
+=head2 Automatic and complete dependency analysis
+
+With Cons, dependencies are handled automatically. Continuing the previous
+example, note that when we modify F<world.c>, F<world.o> is recompiled,
+F<libworld.a> recreated, and F<hello> relinked:
+
+
+
+ % vi world.c
+ [EDIT]
+ % cons hello
+ gcc -c world.c -o world.o
+ ar r libworld.a world.o
+ ar: creating libworld.a
+ ranlib libworld.a
+ gcc -o hello hello.o libworld.a
+
+This is a relatively simple example: Cons ``knows'' F<world.o> depends upon
+F<world.c>, because the dependency is explicitly set up by the C<Library>
+method. It also knows that F<libworld.a> depends upon F<world.o> and that
+F<hello> depends upon F<libworld.a>, all for similar reasons.
+
+Now it turns out that F<hello.c> also includes the interface definition
+file, F<world.h>:
+
+
+
+ % emacs world.h
+ [EDIT]
+ % cons hello
+ gcc -c hello.c -o hello.o
+ gcc -o hello hello.o libworld.a
+
+How does Cons know that F<hello.c> includes F<world.h>, and that F<hello.o>
+must therefore be recompiled? For now, suffice it to say that when
+considering whether or not F<hello.o> is up-to-date, Cons invokes a scanner
+for its dependency, F<hello.c>. This scanner enumerates the files included
+by F<hello.c> to come up with a list of further dependencies, beyond those
+made explicit by the Cons script. This process is recursive: any files
+included by included files will also be scanned.
+
+Isn't this expensive? The answer is--it depends. If you do a full build of a
+large system, the scanning time is insignificant. If you do a rebuild of a
+large system, then Cons will spend a fair amount of time thinking about it
+before it decides that nothing has to be done (although not necessarily more
+time than make!). The good news is that Cons makes it very easy to
+intelligently subset your build, when you are working on localized changes.
+
+
+=head2 Automatic global build sequencing
+
+Because Cons does full and accurate dependency analysis, and does this
+globally, for the entire build, Cons is able to use this information to take
+full control of the B<sequencing> of the build. This sequencing is evident
+in the above examples, and is equivalent to what you would expect for make,
+given a full set of dependencies. With Cons, this extends trivially to
+larger, multi-directory builds. As a result, all of the complexity involved
+in making sure that a build is organized correctly--including multi-pass
+hierarchical builds--is eliminated. We'll discuss this further in the next
+sections.
+
+=head1 Building large trees--still just as simple
+
+
+=head2 A hierarchy of build scripts
+
+A larger build, in Cons, is organized by creating a hierarchy of B<build
+scripts>. At the top of the tree is a script called F<Construct>. The rest
+of the scripts, by convention, are each called F<Conscript>. These scripts
+are connected together, very simply, by the C<Build>, C<Export>, and
+C<Import> commands.
+
+
+=head2 The Build command
+
+The C<Build> command takes a list of F<Conscript> file names, and arranges
+for them to be included in the build. For example:
+
+ Build qw(
+ drivers/display/Conscript
+ drivers/mouse/Conscript
+ parser/Conscript
+ utilities/Conscript
+ );
+
+This is a simple two-level hierarchy of build scripts: all the subsidiary
+F<Conscript> files are mentioned in the top-level F<Construct> file. Notice
+that not all directories in the tree necessarily have build scripts
+associated with them.
+
+This could also be written as a multi-level script. For example, the
+F<Construct> file might contain this command:
+
+ Build qw(
+ parser/Conscript
+ drivers/Conscript
+ utilities/Conscript
+ );
+
+and the F<Conscript> file in the F<drivers> directory might contain this:
+
+ Build qw(
+ display/Conscript
+ mouse/Conscript
+ );
+
+Experience has shown that the former model is a little easier to understand,
+since the whole construction tree is laid out in front of you, at the
+top-level. Hybrid schemes are also possible. A separately maintained
+component that needs to be incorporated into a build tree, for example,
+might hook into the build tree in one place, but define its own construction
+hierarchy.
+
+By default, Cons does not change its working directory to the directory
+containing a subsidiary F<Conscript> file it is including. This behavior
+can be enabled for a build by specifying, in the top-level F<Construct>
+file:
+
+ Conscript_chdir 1;
+
+When enabled, Cons will change to the subsidiary F<Conscript> file's
+containing directory while reading in that file, and then change back
+to the top-level directory once the file has been processed.
+
+It is expected that this behavior will become the default in some future
+version of Cons. To prepare for this transition, builds that expect
+Cons to remain at the top of the build while it reads in a subsidiary
+F<Conscript> file should explicitly disable this feature as follows:
+
+ Conscript_chdir 0;
+
+
+=head2 Relative, top-relative, and absolute file names
+
+You may have noticed that the file names specified to the Build command are
+relative to the location of the script it is invoked from. This is generally
+true for other filename arguments to other commands, too, although we might
+as well mention here that if you begin a file name with a hash mark, ``#'',
+then that file is interpreted relative to the top-level directory (where the
+F<Construct> file resides). And, not surprisingly, if you begin it with ``/'',
+then it is considered to be an absolute pathname. This is true even on
+systems which use a back slash rather than a forward slash to name absolute
+paths.
+
+(There is another file prefix, ``!'', that is interpreted specially by
+Cons. See discussion of the C<Link> command, below, for details.)
+
+
+=head2 Using modules in build scripts
+
+You may pull modules into each F<Conscript> file using the normal Perl
+C<use> or C<require> statements:
+
+ use English;
+ require My::Module;
+
+Each C<use> or C<require> only affects the one F<Conscript> file in which
+it appears. To use a module in multiple F<Conscript> files, you must
+put a C<use> or C<require> statement in each one that needs the module.
+
+
+=head2 Scope of variables
+
+The top-level F<Construct> file and all F<Conscript> files begin life in
+a common, separate Perl package. B<Cons> controls the symbol table for
+the package so that, the symbol table for each script is empty, except
+for the F<Construct> file, which gets some of the command line arguments.
+All of the variables that are set or used, therefore, are set by the
+script itself--not by some external script.
+
+Variables can be explicitly B<imported> by a script from its parent
+script. To import a variable, it must have been B<exported> by the parent
+and initialized (otherwise an error will occur).
+
+
+=head2 The Export command
+
+The C<Export> command is used as in the following example:
+
+ $env = new cons();
+ $INCLUDE = "#export/include";
+ $LIB = "#export/lib";
+ Export qw( env INCLUDE LIB );
+ Build qw( util/Conscript );
+
+The values of the simple variables mentioned in the C<Export> list will be
+squirreled away by any subsequent C<Build> commands. The C<Export> command
+will only export Perl B<scalar> variables, that is, variables whose name
+begins with C<$>. Other variables, objects, etc. can be exported by
+reference--but all scripts will refer to the same object, and this object
+should be considered to be read-only by the subsidiary scripts and by the
+original exporting script. It's acceptable, however, to assign a new value
+to the exported scalar variable--that won't change the underlying variable
+referenced. This sequence, for example, is OK:
+
+ $env = new cons();
+ Export qw( env INCLUDE LIB );
+ Build qw( util/Conscript );
+ $env = new cons(CFLAGS => '-O');
+ Build qw( other/Conscript );
+
+It doesn't matter whether the variable is set before or after the C<Export>
+command. The important thing is the value of the variable at the time the
+C<Build> command is executed. This is what gets squirreled away. Any
+subsequent C<Export> commands, by the way, invalidate the first: you must
+mention all the variables you wish to export on each C<Export> command.
+
+
+=head2 The Import command
+
+Variables exported by the C<Export> command can be imported into subsidiary
+scripts by the C<Import> command. The subsidiary script always imports
+variables directly from the superior script. Consider this example:
+
+ Import qw( env INCLUDE );
+
+This is only legal if the parent script exported both C<$env> and
+C<$INCLUDE>. It also must have given each of these variables values. It is
+OK for the subsidiary script to only import a subset of the exported
+variables (in this example, C<$LIB>, which was exported by the previous
+example, is not imported).
+
+All the imported variables are automatically re-exported, so the sequence:
+
+ Import qw ( env INCLUDE );
+ Build qw ( beneath-me/Conscript );
+
+will supply both C<$env> and C<$INCLUDE> to the subsidiary file. If only
+C<$env> is to be exported, then the following will suffice:
+
+ Import qw ( env INCLUDE );
+ Export qw ( env );
+ Build qw ( beneath-me/Conscript );
+
+Needless to say, the variables may be modified locally before invoking
+C<Build> on the subsidiary script.
+
+
+=head2 Build script evaluation order
+
+The only constraint on the ordering of build scripts is that superior
+scripts are evaluated before their inferior scripts. The top-level
+F<Construct> file, for instance, is evaluated first, followed by any
+inferior scripts. This is all you really need to know about the evaluation
+order, since order is generally irrelevant. Consider the following C<Build>
+command:
+
+ Build qw(
+ drivers/display/Conscript
+ drivers/mouse/Conscript
+ parser/Conscript
+ utilities/Conscript
+ );
+
+We've chosen to put the script names in alphabetical order, simply because
+that's the most convenient for maintenance purposes. Changing the order will
+make no difference to the build.
+
+
+=head1 A Model for sharing files
+
+
+=head2 Some simple conventions
+
+In any complex software system, a method for sharing build products needs to
+be established. We propose a simple set of conventions which are trivial to
+implement with Cons, but very effective.
+
+The basic rule is to require that all build products which need to be shared
+between directories are shared via an intermediate directory. We have
+typically called this F<export>, and, in a C environment, provided
+conventional sub-directories of this directory, such as F<include>, F<lib>,
+F<bin>, etc.
+
+These directories are defined by the top-level F<Construct> file. A simple
+F<Construct> file for a B<Hello, World!> application, organized using
+multiple directories, might look like this:
+
+ # Construct file for Hello, World!
+
+ # Where to put all our shared products.
+ $EXPORT = '#export';
+
+ Export qw( CONS INCLUDE LIB BIN );
+
+ # Standard directories for sharing products.
+ $INCLUDE = "$EXPORT/include";
+ $LIB = "$EXPORT/lib";
+ $BIN = "$EXPORT/bin";
+
+ # A standard construction environment.
+ $CONS = new cons (
+ CPPPATH => $INCLUDE, # Include path for C Compilations
+ LIBPATH => $LIB, # Library path for linking programs
+ LIBS => '-lworld', # List of standard libraries
+ );
+
+ Build qw(
+ hello/Conscript
+ world/Conscript
+ );
+
+The F<world> directory's F<Conscript> file looks like this:
+
+ # Conscript file for directory world
+ Import qw( CONS INCLUDE LIB );
+
+ # Install the products of this directory
+ Install $CONS $LIB, 'libworld.a';
+ Install $CONS $INCLUDE, 'world.h';
+
+ # Internal products
+ Library $CONS 'libworld.a', 'world.c';
+
+and the F<hello> directory's F<Conscript> file looks like this:
+
+ # Conscript file for directory hello
+ Import qw( CONS BIN );
+
+ # Exported products
+ Install $CONS $BIN, 'hello';
+
+ # Internal products
+ Program $CONS 'hello', 'hello.c';
+
+To construct a B<Hello, World!> program with this directory structure, go to
+the top-level directory, and invoke C<cons> with the appropriate
+arguments. In the following example, we tell Cons to build the directory
+F<export>. To build a directory, Cons recursively builds all known products
+within that directory (only if they need rebuilding, of course). If any of
+those products depend upon other products in other directories, then those
+will be built, too.
+
+ % cons export
+ Install world/world.h as export/include/world.h
+ cc -Iexport/include -c hello/hello.c -o hello/hello.o
+ cc -Iexport/include -c world/world.c -o world/world.o
+ ar r world/libworld.a world/world.o
+ ar: creating world/libworld.a
+ ranlib world/libworld.a
+ Install world/libworld.a as export/lib/libworld.a
+ cc -o hello/hello hello/hello.o -Lexport/lib -lworld
+ Install hello/hello as export/bin/hello
+
+
+=head2 Clean, understandable, location-independent scripts
+
+You'll note that the two F<Conscript> files are very clean and
+to-the-point. They simply specify products of the directory and how to build
+those products. The build instructions are minimal: they specify which
+construction environment to use, the name of the product, and the name of
+the inputs. Note also that the scripts are location-independent: if you wish
+to reorganize your source tree, you are free to do so: you only have to
+change the F<Construct> file (in this example), to specify the new locations
+of the F<Conscript> files. The use of an export tree makes this goal easy.
+
+Note, too, how Cons takes care of little details for you. All the F<export>
+directories, for example, were made automatically. And the installed files
+were really hard-linked into the respective export directories, to save
+space and time. This attention to detail saves considerable work, and makes
+it even easier to produce simple, maintainable scripts.
+
+
+=head1 Separating source and build trees
+
+It's often desirable to keep any derived files from the build completely
+separate from the source files. This makes it much easier to keep track of
+just what is a source file, and also makes it simpler to handle B<variant>
+builds, especially if you want the variant builds to co-exist.
+
+
+=head2 Separating build and source directories using the Link command
+
+Cons provides a simple mechanism that handles all of these requirements. The
+C<Link> command is invoked as in this example:
+
+ Link 'build' => 'src';
+
+The specified directories are ``linked'' to the specified source
+directory. Let's suppose that you setup a source directory, F<src>, with the
+sub-directories F<world> and F<hello> below it, as in the previous
+example. You could then substitute for the original build lines the
+following:
+
+ Build qw(
+ build/world/Conscript
+ build/hello/Conscript
+ );
+
+Notice that you treat the F<Conscript> file as if it existed in the build
+directory. Now if you type the same command as before, you will get the
+following results:
+
+ % cons export
+ Install build/world/world.h as export/include/world.h
+ cc -Iexport/include -c build/hello/hello.c -o build/hello/hello.o
+ cc -Iexport/include -c build/world/world.c -o build/world/world.o
+ ar r build/world/libworld.a build/world/world.o
+ ar: creating build/world/libworld.a
+ ranlib build/world/libworld.a
+ Install build/world/libworld.a as export/lib/libworld.a
+ cc -o build/hello/hello build/hello/hello.o -Lexport/lib -lworld
+ Install build/hello/hello as export/bin/hello
+
+Again, Cons has taken care of the details for you. In particular, you will
+notice that all the builds are done using source files and object files from
+the build directory. For example, F<build/world/world.o> is compiled from
+F<build/world/world.c>, and F<export/include/world.h> is installed from
+F<build/world/world.h>. This is accomplished on most systems by the simple
+expedient of ``hard'' linking the required files from each source directory
+into the appropriate build directory.
+
+The links are maintained correctly by Cons, no matter what you do to the
+source directory. If you modify a source file, your editor may do this ``in
+place'' or it may rename it first and create a new file. In the latter case,
+any hard link will be lost. Cons will detect this condition the next time
+the source file is needed, and will relink it appropriately.
+
+You'll also notice, by the way, that B<no> changes were required to the
+underlying F<Conscript> files. And we can go further, as we shall see in the
+next section.
+
+=head2 Explicit references to the source directory
+
+When using the C<Link> command on some operating systems or with some
+tool chains, it's sometimes useful to have a command actually use
+the path name to the source directory, not the build directory. For
+example, on systems that must copy, not "hard link," the F<src/> and
+F<build/> copies of C<Linked> files, using the F<src/> path of a file
+name might make an editor aware that a syntax error must be fixed in the
+source directory, not the build directory.
+
+You can tell Cons that you want to use the "source path" for a file by
+preceding the file name with a ``!'' (exclamation point). For example,
+if we add a ``!'' to the beginning of a source file:
+
+ Program $env "foo", "!foo.c"; # Notice initial ! on foo.c
+
+Cons will compile the target as follows:
+
+ cc -c src/foo.c -o build/foo.o
+ cc -o build/foo build/foo.o
+
+Notice that Cons has compiled the program from the the F<src/foo.c>
+source file. Without the initial ``!'', Cons would have compiled the
+program using the F<build/foo.c> path name.
+
+
+
+=head1 Variant builds
+
+
+=head2 Hello, World! for baNaNa and peAcH OS's
+
+Variant builds require just another simple extension. Let's take as an
+example a requirement to allow builds for both the baNaNa and peAcH
+operating systems. In this case, we are using a distributed file system,
+such as NFS to access the particular system, and only one or the other of
+the systems has to be compiled for any given invocation of C<cons>. Here's
+one way we could set up the F<Construct> file for our B<Hello, World!>
+application:
+
+ # Construct file for Hello, World!
+
+ die qq(OS must be specified) unless $OS = $ARG{OS};
+ die qq(OS must be "peach" or "banana")
+ if $OS ne "peach" && $OS ne "banana";
+
+ # Where to put all our shared products.
+ $EXPORT = "#export/$OS";
+
+ Export qw( CONS INCLUDE LIB BIN );
+
+ # Standard directories for sharing products.
+ $INCLUDE = "$EXPORT/include";
+ $LIB = "$EXPORT/lib";
+ $BIN = "$EXPORT/bin";
+
+ # A standard construction environment.
+ $CONS = new cons (
+ CPPPATH => $INCLUDE, # Include path for C Compilations
+ LIBPATH => $LIB, # Library path for linking programs
+ LIBS => '-lworld', # List of standard libraries
+ );
+
+ # $BUILD is where we will derive everything.
+ $BUILD = "#build/$OS";
+
+ # Tell cons where the source files for $BUILD are.
+ Link $BUILD => 'src';
+
+ Build (
+ "$BUILD/hello/Conscript",
+ "$BUILD/world/Conscript",
+ );
+
+Now if we login to a peAcH system, we can build our B<Hello, World!>
+application for that platform:
+
+ % cons export OS=peach
+ Install build/peach/world/world.h as export/peach/include/world.h
+ cc -Iexport/peach/include -c build/peach/hello/hello.c -o build/peach/hello/hello.o
+ cc -Iexport/peach/include -c build/peach/world/world.c -o build/peach/world/world.o
+ ar r build/peach/world/libworld.a build/peach/world/world.o
+ ar: creating build/peach/world/libworld.a
+ ranlib build/peach/world/libworld.a
+ Install build/peach/world/libworld.a as export/peach/lib/libworld.a
+ cc -o build/peach/hello/hello build/peach/hello/hello.o -Lexport/peach/lib -lworld
+ Install build/peach/hello/hello as export/peach/bin/hello
+
+
+=head2 Variations on a theme
+
+Other variations of this model are possible. For example, you might decide
+that you want to separate out your include files into platform dependent and
+platform independent files. In this case, you'd have to define an
+alternative to C<$INCLUDE> for platform-dependent files. Most F<Conscript>
+files, generating purely platform-independent include files, would not have
+to change.
+
+You might also want to be able to compile your whole system with debugging
+or profiling, for example, enabled. You could do this with appropriate
+command line options, such as C<DEBUG=on>. This would then be translated
+into the appropriate platform-specific requirements to enable debugging
+(this might include turning off optimization, for example). You could
+optionally vary the name space for these different types of systems, but, as
+we'll see in the next section, it's not B<essential> to do this, since Cons
+is pretty smart about rebuilding things when you change options.
+
+
+=head1 Signatures
+
+Cons uses file B<signatures> to decide if a derived file is out-of-date
+and needs rebuilding. In essence, if the contents of a file change,
+or the manner in which the file is built changes, the file's signature
+changes as well. This allows Cons to decide with certainty when a file
+needs rebuilding, because Cons can detect, quickly and reliably, whether
+any of its dependency files have been changed.
+
+
+=head2 MD5 content and build signatures
+
+Cons uses the B<MD5> (B<Message Digest 5>) algorithm to compute file
+signatures. The MD5 algorithm computes a strong cryptographic checksum
+for any given input string. Cons can, based on configuration, use two
+different MD5 signatures for a given file:
+
+The B<content signature> of a file is an MD5 checksum of the file's
+contents. Consequently, when the contents of a file change, its content
+signature changes as well.
+
+The B<build signature> of a file is a combined MD5 checksum of:
+
+=over 4
+
+the signatures of all the input files used to build the file
+
+the signatures of all dependency files discovered by source scanners
+(for example, C<.h> files)
+
+the signatures of all dependency files specified explicitly via the
+C<Depends> method)
+
+the command-line string used to build the file
+
+=back
+
+The build signature is, in effect, a digest of all the dependency
+information for the specified file. Consequently, a file's build
+signature changes whenever any part of its dependency information
+changes: a new file is added, the contents of a file on which it depends
+change, there's a change to the command line used to build the file (or
+any of its dependency files), etc.
+
+For example, in the previous section, the build signature of the
+F<world.o> file will include:
+
+=over 4
+
+the signature of the F<world.c> file
+
+the signatures of any header files that Cons detects are included,
+directly or indirectly, by F<world.c>
+
+the text of the actual command line was used to generate F<world.o>
+
+=back
+
+Similarly, the build signature of the F<libworld.a> file will include
+all the signatures of its constituents (and hence, transitively, the
+signatures of B<their> constituents), as well as the command line that
+created the file.
+
+Note that there is no need for a derived file to depend upon any
+particular F<Construct> or F<Conscript> file. If changes to these files
+affect a file, then this will be automatically reflected in its build
+signature, since relevant parts of the command line are included in the
+signature. Unrelated F<Construct> or F<Conscript> changes will have no
+effect.
+
+
+=head2 Storing signatures in .consign files
+
+Before Cons exits, it stores the calculated signatures for all of the
+files it built or examined in F<.consign> files, one per directory.
+Cons uses this stored information on later invocations to decide if
+derived files need to be rebuilt.
+
+After the previous example was compiled, the F<.consign> file in the
+F<build/peach/world> directory looked like this:
+
+ world.h:985533370 - d181712f2fdc07c1f05d97b16bfad904
+ world.o:985533372 2a0f71e0766927c0532977b0d2158981
+ world.c:985533370 - c712f77189307907f4189b5a7ab62ff3
+ libworld.a:985533374 69e568fc5241d7d25be86d581e1fb6aa
+
+After the file name and colon, the first number is a timestamp of the
+file's modification time (on UNIX systems, this is typically the number
+of seconds since January 1st, 1970). The second value is the build
+signature of the file (or ``-'' in the case of files with no build
+signature--that is, source files). The third value, if any, is the
+content signature of the file.
+
+
+=head2 Using build signatures to decide when to rebuild files
+
+When Cons is deciding whether to build or rebuild a derived file, it
+first computes the file's current build signature. If the file doesn't
+exist, it must obviously be built.
+
+If, however, the file already exists, Cons next compares the
+modification timestamp of the file against the timestamp value in
+the F<.consign> file. If the timestamps match, Cons compares the
+newly-computed build signature against the build signature in the
+F<.consign> file. If the timestamps do not match or the build
+signatures do not match, the derived file is rebuilt.
+
+After the file is built or rebuilt, Cons arranges to store the
+newly-computed build signature in the F<.consign> file when it exits.
+
+
+=head2 Signature example
+
+The use of these signatures is an extremely simple, efficient, and
+effective method of improving--dramatically--the reproducibility of a
+system.
+
+We'll demonstrate this with a simple example:
+
+ # Simple "Hello, World!" Construct file
+ $CFLAGS = '-g' if $ARG{DEBUG} eq 'on';
+ $CONS = new cons(CFLAGS => $CFLAGS);
+ Program $CONS 'hello', 'hello.c';
+
+Notice how Cons recompiles at the appropriate times:
+
+ % cons hello
+ cc -c hello.c -o hello.o
+ cc -o hello hello.o
+ % cons hello
+ cons: "hello" is up-to-date.
+ % cons DEBUG=on hello
+ cc -g -c hello.c -o hello.o
+ cc -o hello hello.o
+ % cons DEBUG=on hello
+ cons: "hello" is up-to-date.
+ % cons hello
+ cc -c hello.c -o hello.o
+ cc -o hello hello.o
+
+
+=head2 Source-file signature configuration
+
+Cons provides a C<SourceSignature> method that allows you to configure
+how the signature should be calculated for any source file when its
+signature is being used to decide if a dependent file is up-to-date.
+The arguments to the C<SourceSignature> method consist of one or more
+pairs of strings:
+
+ SourceSignature 'auto/*.c' => 'content',
+ '*' => 'stored-content';
+
+The first string in each pair is a pattern to match against derived file
+path names. The pattern is a file-globbing pattern, not a Perl regular
+expression; the pattern <*.l> will match all Lex source files. The C<*>
+wildcard will match across directory separators; the pattern C<foo/*.c>
+would match all C source files in any subdirectory underneath the C<foo>
+subdirectory.
+
+The second string in each pair contains one of the following keywords to
+specify how signatures should be calculated for source files that match
+the pattern. The available keywords are:
+
+=over 4
+
+=item content
+
+Use the content signature of the source file when calculating signatures
+of files that depend on it. This guarantees correct calculation of the
+file's signature for all builds, by telling Cons to read the contents of
+a source file to calculate its content signature each time it is run.
+
+=item stored-content
+
+Use the source file's content signature as stored in the F<.consign>
+file, provided the file's timestamp matches the cached timestamp value
+in the F<.consign> file. This optimizes performance, with the slight
+risk of an incorrect build if a source file's contents have been changed
+so quickly after its previous update that the timestamp still matches
+the stored timestamp in the F<.consign> file even though the contents
+have changed.
+
+=back
+
+The Cons default behavior of always calculating a source file's
+signature from the file's contents is equivalent to specifying:
+
+ SourceSignature '*' => 'content';
+
+The C<*> will match all source files. The C<content> keyword
+specifies that Cons will read the contents of a source file to calculate
+its signature each time it is run.
+
+A useful global performance optimization is:
+
+ SourceSignature '*' => 'stored-content';
+
+This specifies that Cons will use pre-computed content signatures
+from F<.consign> files, when available, rather than re-calculating a
+signature from the the source file's contents each time Cons is run. In
+practice, this is safe for most build situations, and only a problem
+when source files are changed automatically (by scripts, for example).
+The Cons default, however, errs on the side of guaranteeing a correct
+build in all situations.
+
+Cons tries to match source file path names against the patterns in the
+order they are specified in the C<SourceSignature> arguments:
+
+ SourceSignature '/usr/repository/objects/*' => 'stored-content',
+ '/usr/repository/*' => 'content',
+ '*.y' => 'content',
+ '*' => 'stored-content';
+
+In this example, all source files under the F</usr/repository/objects>
+directory will use F<.consign> file content signatures, source files
+anywhere else underneath F</usr/repository> will not use F<.consign>
+signature values, all Yacc source files (C<*.y>) anywhere else will not
+use F<.consign> signature values, and any other source file will use
+F<.consign> signature values.
+
+
+=head2 Derived-file signature configuration
+
+Cons provides a C<SIGNATURE> construction variable that allows you to
+configure how signatures are calculated for any derived file when its
+signature is being used to decide if a dependent file is up-to-date.
+The value of the C<SIGNATURE> construction variable is a Perl array
+reference that holds one or more pairs of strings, like the arguments to
+the C<SourceSignature> method.
+
+The first string in each pair is a pattern to match against derived file
+path names. The pattern is a file-globbing pattern, not a Perl regular
+expression; the pattern `*.obj' will match all (Win32) object files.
+The C<*> wildcard will match across directory separators; the pattern
+`foo/*.a' would match all (UNIX) library archives in any subdirectory
+underneath the foo subdirectory.
+
+The second string in each pair contains one of the following keywords
+to specify how signatures should be calculated for derived files that
+match the pattern. The available keywords are the same as for the
+C<SourceSignature> method, with an additional keyword:
+
+=over 4
+
+=item build
+
+Use the build signature of the derived file when calculating signatures
+of files that depend on it. This guarantees correct builds by forcing
+Cons to rebuild any and all files that depend on the derived file.
+
+=item content
+
+Use the content signature of the derived file when calculating signatures
+of files that depend on it. This guarantees correct calculation of the
+file's signature for all builds, by telling Cons to read the contents of
+a derived file to calculate its content signature each time it is run.
+
+=item stored-content
+
+Use the derived file's content signature as stored in the F<.consign>
+file, provided the file's timestamp matches the cached timestamp value
+in the F<.consign> file. This optimizes performance, with the slight
+risk of an incorrect build if a derived file's contents have been
+changed so quickly after a Cons build that the file's timestamp still
+matches the stored timestamp in the F<.consign> file.
+
+=back
+
+The Cons default behavior (as previously described) for using
+derived-file signatures is equivalent to:
+
+ $env = new cons(SIGNATURE => ['*' => 'build']);
+
+The C<*> will match all derived files. The C<build> keyword specifies
+that all derived files' build signatures will be used when calculating
+whether a dependent file is up-to-date.
+
+A useful alternative default C<SIGNATURE> configuration for many sites:
+
+ $env = new cons(SIGNATURE => ['*' => 'content']);
+
+In this configuration, derived files have their signatures calculated
+from the file contents. This adds slightly to Cons' workload, but has
+the useful effect of "stopping" further rebuilds if a derived file is
+rebuilt to exactly the same file contents as before, which usually
+outweighs the additional computation Cons must perform.
+
+For example, changing a comment in a C file and recompiling should
+generate the exact same object file (assuming the compiler doesn't
+insert a timestamp in the object file's header). In that case,
+specifying C<content> or C<stored-content> for the signature calculation
+will cause Cons to recognize that the object file did not actually
+change as a result of being rebuilt, and libraries or programs that
+include the object file will not be rebuilt. When C<build> is
+specified, however, Cons will only "know" that the object file was
+rebuilt, and proceed to rebuild any additional files that include the
+object file.
+
+Note that Cons tries to match derived file path names against the
+patterns in the order they are specified in the C<SIGNATURE> array
+reference:
+
+ $env = new cons(SIGNATURE => ['foo/*.o' => 'build',
+ '*.o' => 'content',
+ '*.a' => 'stored-content',
+ '*' => 'content']);
+
+In this example, all object files underneath the F<foo> subdirectory
+will use build signatures, all other object files (including object
+files underneath other subdirectories!) will use F<.consign> file
+content signatures, libraries will use F<.consign> file build
+signatures, and all other derived files will use content signatures.
+
+
+=head2 Debugging signature calculation
+
+Cons provides a C<-S> option that can be used to specify what internal
+Perl package Cons should use to calculate signatures. The default Cons
+behavior is equivalent to specifying C<-S md5> on the command line.
+
+The only other package (currently) available is an C<md5::debug>
+package that prints out detailed information about the MD5 signature
+calculations performed by Cons:
+
+ % cons -S md5::debug hello
+ sig::md5::srcsig(hello.c)
+ => |52d891204c62fe93ecb95281e1571938|
+ sig::md5::collect(52d891204c62fe93ecb95281e1571938)
+ => |fb0660af4002c40461a2f01fbb5ffd03|
+ sig::md5::collect(52d891204c62fe93ecb95281e1571938,
+ fb0660af4002c40461a2f01fbb5ffd03,
+ cc -c %< -o %>)
+ => |f7128da6c3fe3c377dc22ade70647b39|
+ sig::md5::current(||
+ eq |f7128da6c3fe3c377dc22ade70647b39|)
+ cc -c hello.c -o hello.o
+ sig::md5::collect()
+ => |d41d8cd98f00b204e9800998ecf8427e|
+ sig::md5::collect(f7128da6c3fe3c377dc22ade70647b39,
+ d41d8cd98f00b204e9800998ecf8427e,
+ cc -o %> %< )
+ => |a0bdce7fd09e0350e7efbbdb043a00b0|
+ sig::md5::current(||
+ eq |a0bdce7fd09e0350e7efbbdb043a00b0|)
+ cc -o hello, hello.o
+
+
+=head1 Code Repositories
+
+Many software development organizations will have one or more central
+repository directory trees containing the current source code for one or
+more projects, as well as the derived object files, libraries, and
+executables. In order to reduce unnecessary recompilation, it is useful to
+use files from the repository to build development software--assuming, of
+course, that no newer dependency file exists in the local build tree.
+
+
+=head2 Repository
+
+Cons provides a mechanism to specify a list of code repositories that will
+be searched, in-order, for source files and derived files not found in the
+local build directory tree.
+
+The following lines in a F<Construct> file will instruct Cons to look first
+under the F</usr/experiment/repository> directory and then under the
+F</usr/product/repository> directory:
+
+ Repository qw (
+ /usr/experiment/repository
+ /usr/product/repository
+ );
+
+The repository directories specified may contain source files, derived files
+(objects, libraries and executables), or both. If there is no local file
+(source or derived) under the directory in which Cons is executed, then the
+first copy of a same-named file found under a repository directory will be
+used to build any local derived files.
+
+Cons maintains one global list of repositories directories. Cons will
+eliminate the current directory, and any non-existent directories, from the
+list.
+
+
+=head2 Finding the Construct file in a Repository
+
+Cons will also search for F<Construct> and F<Conscript> files in the
+repository tree or trees. This leads to a chicken-and-egg situation,
+though: how do you look in a repository tree for a F<Construct> file if the
+F<Construct> file tells you where the repository is? To get around this,
+repositories may be specified via C<-R> options on the command line:
+
+ % cons -R /usr/experiment/repository -R /usr/product/repository .
+
+Any repository directories specified in the F<Construct> or F<Conscript>
+files will be appended to the repository directories specified by
+command-line C<-R> options.
+
+=head2 Repository source files
+
+If the source code (include the F<Conscript> file) for the library version
+of the I<Hello, World!> C application is in a repository (with no derived
+files), Cons will use the repository source files to create the local object
+files and executable file:
+
+ % cons -R /usr/src_only/repository hello
+ gcc -c /usr/src_only/repository/hello.c -o hello.o
+ gcc -c /usr/src_only/repository/world.c -o world.o
+ ar r libworld.a world.o
+ ar: creating libworld.a
+ ranlib libworld.a
+ gcc -o hello hello.o libworld.a
+
+Creating a local source file will cause Cons to rebuild the appropriate
+derived file or files:
+
+ % pico world.c
+ [EDIT]
+ % cons -R /usr/src_only/repository hello
+ gcc -c world.c -o world.o
+ ar r libworld.a world.o
+ ar: creating libworld.a
+ ranlib libworld.a
+ gcc -o hello hello.o libworld.a
+
+And removing the local source file will cause Cons to revert back to
+building the derived files from the repository source:
+
+ % rm world.c
+ % cons -R /usr/src_only/repository hello
+ gcc -c /usr/src_only/repository/world.c -o world.o
+ ar r libworld.a world.o
+ ar: creating libworld.a
+ ranlib libworld.a
+ gcc -o hello hello.o libworld.a
+
+
+=head2 Repository derived files
+
+If a repository tree contains derived files (usually object files,
+libraries, or executables), Cons will perform its normal signature
+calculation to decide whether the repository file is up-to-date or a derived
+file must be built locally. This means that, in order to ensure correct
+signature calculation, a repository tree must also contain the F<.consign>
+files that were created by Cons when generating the derived files.
+
+This would usually be accomplished by building the software in the
+repository (or, alternatively, in a build directory, and then copying the
+result to the repository):
+
+ % cd /usr/all/repository
+ % cons hello
+ gcc -c hello.c -o hello.o
+ gcc -c world.c -o world.o
+ ar r libworld.a world.o
+ ar: creating libworld.a
+ ranlib libworld.a
+ gcc -o hello hello.o libworld.a
+
+(This is safe even if the F<Construct> file lists the F</usr/all/repository>
+directory in a C<Repository> command because Cons will remove the current
+directory from the repository list.)
+
+Now if we want to build a copy of the application with our own F<hello.c>
+file, we only need to create the one necessary source file, and use the
+C<-R> option to have Cons use other files from the repository:
+
+ % mkdir $HOME/build1
+ % cd $HOME/build1
+ % ed hello.c
+ [EDIT]
+ % cons -R /usr/all/repository hello
+ gcc -c hello.c -o hello.o
+ gcc -o hello hello.o /usr/all/repository/libworld.a
+
+Notice that Cons has not bothered to recreate a local F<libworld.a> library
+(or recompile the F<world.o> module), but instead uses the already-compiled
+version from the repository.
+
+Because the MD5 signatures that Cons puts in the F<.consign> file contain
+timestamps for the derived files, the signature timestamps must match the
+file timestamps for a signature to be considered valid.
+
+Some software systems may alter the timestamps on repository files (by
+copying them, e.g.), in which case Cons will, by default, assume the
+repository signatures are invalid and rebuild files unnecessarily. This
+behavior may be altered by specifying:
+
+ Repository_Sig_Times_OK 0;
+
+This tells Cons to ignore timestamps when deciding whether a signature is
+valid. (Note that avoiding this sanity check means there must be proper
+control over the repository tree to ensure that the derived files cannot be
+modified without updating the F<.consign> signature.)
+
+
+=head2 Local copies of files
+
+If the repository tree contains the complete results of a build, and we try
+to build from the repository without any files in our local tree, something
+moderately surprising happens:
+
+ % mkdir $HOME/build2
+ % cd $HOME/build2
+ % cons -R /usr/all/repository hello
+ cons: "hello" is up-to-date.
+
+Why does Cons say that the F<hello> program is up-to-date when there is no
+F<hello> program in the local build directory? Because the repository (not
+the local directory) contains the up-to-date F<hello> program, and Cons
+correctly determines that nothing needs to be done to rebuild this
+up-to-date copy of the file.
+
+There are, however, many times in which it is appropriate to ensure that a
+local copy of a file always exists. A packaging or testing script, for
+example, may assume that certain generated files exist locally. Instead of
+making these subsidiary scripts aware of the repository directory, the
+C<Local> command may be added to a F<Construct> or F<Conscript> file to
+specify that a certain file or files must appear in the local build
+directory:
+
+ Local qw(
+ hello
+ );
+
+Then, if we re-run the same command, Cons will make a local copy of the
+program from the repository copy (telling you that it is doing so):
+
+ % cons -R /usr/all/repository hello
+ Local copy of hello from /usr/all/repository/hello
+ cons: "hello" is up-to-date.
+
+Notice that, because the act of making the local copy is not considered a
+"build" of the F<hello> file, Cons still reports that it is up-to-date.
+
+Creating local copies is most useful for files that are being installed into
+an intermediate directory (for sharing with other directories) via the
+C<Install> command. Accompanying the C<Install> command for a file with a
+companion C<Local> command is so common that Cons provides a
+C<Install_Local> command as a convenient way to do both:
+
+ Install_Local $env, '#export', 'hello';
+
+is exactly equivalent to:
+
+ Install $env '#export', 'hello';
+ Local '#export/hello';
+
+Both the C<Local> and C<Install_Local> commands update the local F<.consign>
+file with the appropriate file signatures, so that future builds are
+performed correctly.
+
+
+=head2 Repository dependency analysis
+
+Due to its built-in scanning, Cons will search the specified repository
+trees for included F<.h> files. Unless the compiler also knows about the
+repository trees, though, it will be unable to find F<.h> files that only
+exist in a repository. If, for example, the F<hello.c> file includes the
+F<hello.h> file in its current directory:
+
+ % cons -R /usr/all/repository hello
+ gcc -c /usr/all/repository/hello.c -o hello.o
+ /usr/all/repository/hello.c:1: hello.h: No such file or directory
+
+Solving this problem forces some requirements onto the way construction
+environments are defined and onto the way the C C<#include> preprocessor
+directive is used to include files.
+
+In order to inform the compiler about the repository trees, Cons will add
+appropriate C<-I> flags to the compilation commands. This means that the
+C<CPPPATH> variable in the construction environment must explicitly specify
+all subdirectories which are to be searched for included files, including the
+current directory. Consequently, we can fix the above example by changing
+the environment creation in the F<Construct> file as follows:
+
+ $env = new cons(
+ CC => 'gcc',
+ CPPPATH => '.',
+ LIBS => 'libworld.a',
+ );
+
+Due to the definition of the C<CPPPATH> variable, this yields, when we
+re-execute the command:
+
+ % cons -R /usr/all/repository hello
+ gcc -c -I. -I/usr/all/repository /usr/all/repository/hello.c -o hello.o
+ gcc -o hello hello.o /usr/all/repository/libworld.a
+
+The order of the C<-I> flags replicates, for the C preprocessor, the same
+repository-directory search path that Cons uses for its own dependency
+analysis. If there are multiple repositories and multiple C<CPPPATH>
+directories, Cons will append the repository directories to the beginning of
+each C<CPPPATH> directory, rapidly multiplying the number of C<-I> flags.
+As an extreme example, a F<Construct> file containing:
+
+ Repository qw(
+ /u1
+ /u2
+ );
+
+ $env = new cons(
+ CPPPATH => 'a:b:c',
+ );
+
+Would yield a compilation command of:
+
+ cc -Ia -I/u1/a -I/u2/a -Ib -I/u1/b -I/u2/b -Ic -I/u1/c -I/u2/c -c hello.c -o hello.o
+
+In order to shorten the command lines as much as possible, Cons will
+remove C<-I> flags for any directories, locally or in the repositories,
+which do not actually exist. (Note that the C<-I> flags are not included
+in the MD5 signature calculation for the target file, so the target will
+not be recompiled if the compilation command changes due to a directory
+coming into existence.)
+
+Because Cons relies on the compiler's C<-I> flags to communicate the
+order in which repository directories must be searched, Cons' handling
+of repository directories is fundamentally incompatible with using
+double-quotes on the C<#include> directives in any C source code that
+you plan to modify:
+
+ #include "file.h" /* DON'T USE DOUBLE-QUOTES LIKE THIS */
+
+This is because most C preprocessors, when faced with such a directive, will
+always first search the directory containing the source file. This
+undermines the elaborate C<-I> options that Cons constructs to make the
+preprocessor conform to its preferred search path.
+
+Consequently, when using repository trees in Cons, B<always> use
+angle-brackets for included files in any C source (.c or .h) files that
+you plan to modify locally:
+
+ #include <file.h> /* USE ANGLE-BRACKETS INSTEAD */
+
+Code that will not change can still safely use double quotes on #include
+lines.
+
+
+=head2 Repository_List
+
+Cons provides a C<Repository_List> command to return a list of all
+repository directories in their current search order. This can be used for
+debugging, or to do more complex Perl stuff:
+
+ @list = Repository_List;
+ print join(' ', @list), "\n";
+
+
+=head2 Repository interaction with other Cons features
+
+Cons' handling of repository trees interacts correctly with other Cons
+features--which is to say, it generally does what you would expect.
+
+Most notably, repository trees interact correctly, and rather powerfully,
+with the 'Link' command. A repository tree may contain one or more
+subdirectories for version builds established via C<Link> to a source
+subdirectory. Cons will search for derived files in the appropriate build
+subdirectories under the repository tree.
+
+
+=head1 Default targets
+
+Until now, we've demonstrated invoking Cons with an explicit target
+to build:
+
+ % cons hello
+
+Normally, Cons does not build anything unless a target is specified,
+but specifying '.' (the current directory) will build everything:
+
+ % cons # does not build anything
+
+ % cons . # builds everything under the top-level directory
+
+Adding the C<Default> method to any F<Construct> or F<Conscript> file will add
+the specified targets to a list of default targets. Cons will build
+these defaults if there are no targets specified on the command line.
+So adding the following line to the top-level F<Construct> file will mimic
+Make's typical behavior of building everything by default:
+
+ Default '.';
+
+The following would add the F<hello> and F<goodbye> commands (in the
+same directory as the F<Construct> or F<Conscript> file) to the default list:
+
+ Default qw(
+ hello
+ goodbye
+ );
+
+The C<Default> method may be used more than once to add targets to the
+default list.
+
+=head1 Selective builds
+
+Cons provides two methods for reducing the size of given build. The first is
+by specifying targets on the command line, and the second is a method for
+pruning the build tree. We'll consider target specification first.
+
+
+=head2 Selective targeting
+
+Like make, Cons allows the specification of ``targets'' on the command
+line. Cons targets may be either files or directories. When a directory is
+specified, this is simply a short-hand notation for every derivable
+product--that Cons knows about--in the specified directory and below. For
+example:
+
+ % cons build/hello/hello.o
+
+means build F<hello.o> and everything that F<hello.o> might need. This is
+from a previous version of the B<Hello, World!> program in which F<hello.o>
+depended upon F<export/include/world.h>. If that file is not up-to-date
+(because someone modified F<src/world/world.h)>, then it will be rebuilt,
+even though it is in a directory remote from F<build/hello>.
+
+In this example:
+
+ % cons build
+
+Everything in the F<build> directory is built, if necessary. Again, this may
+cause more files to be built. In particular, both F<export/include/world.h>
+and F<export/lib/libworld.a> are required by the F<build/hello> directory,
+and so they will be built if they are out-of-date.
+
+If we do, instead:
+
+ % cons export
+
+then only the files that should be installed in the export directory will be
+rebuilt, if necessary, and then installed there. Note that C<cons build>
+might build files that C<cons export> doesn't build, and vice-versa.
+
+
+=head2 No ``special'' targets
+
+With Cons, make-style ``special'' targets are not required. The simplest
+analog with Cons is to use special F<export> directories, instead. Let's
+suppose, for example, that you have a whole series of unit tests that are
+associated with your code. The tests live in the source directory near the
+code. Normally, however, you don't want to build these tests. One solution
+is to provide all the build instructions for creating the tests, and then to
+install the tests into a separate part of the tree. If we install the tests
+in a top-level directory called F<tests>, then:
+
+ % cons tests
+
+will build all the tests.
+
+ % cons export
+
+will build the production version of the system (but not the tests), and:
+
+ % cons build
+
+should probably be avoided (since it will compile tests unnecessarily).
+
+If you want to build just a single test, then you could explicitly name the
+test (in either the F<tests> directory or the F<build> directory). You could
+also aggregate the tests into a convenient hierarchy within the tests
+directory. This hierarchy need not necessarily match the source hierarchy,
+in much the same manner that the include hierarchy probably doesn't match
+the source hierarchy (the include hierarchy is unlikely to be more than two
+levels deep, for C programs).
+
+If you want to build absolutely everything in the tree (subject to whatever
+options you select), you can use:
+
+ % cons .
+
+This is not particularly efficient, since it will redundantly walk all the
+trees, including the source tree. The source tree, of course, may have
+buildable objects in it--nothing stops you from doing this, even if you
+normally build in a separate build tree.
+
+
+=head1 Build Pruning
+
+In conjunction with target selection, B<build pruning> can be used to reduce
+the scope of the build. In the previous peAcH and baNaNa example, we have
+already seen how script-driven build pruning can be used to make only half
+of the potential build available for any given invocation of C<cons>. Cons
+also provides, as a convenience, a command line convention that allows you
+to specify which F<Conscript> files actually get ``built''--that is,
+incorporated into the build tree. For example:
+
+ % cons build +world
+
+The C<+> argument introduces a Perl regular expression. This must, of
+course, be quoted at the shell level if there are any shell meta-characters
+within the expression. The expression is matched against each F<Conscript>
+file which has been mentioned in a C<Build> statement, and only those
+scripts with matching names are actually incorporated into the build
+tree. Multiple such arguments are allowed, in which case a match against any
+of them is sufficient to cause a script to be included.
+
+In the example, above, the F<hello> program will not be built, since Cons
+will have no knowledge of the script F<hello/Conscript>. The F<libworld.a>
+archive will be built, however, if need be.
+
+There are a couple of uses for build pruning via the command line. Perhaps
+the most useful is the ability to make local changes, and then, with
+sufficient knowledge of the consequences of those changes, restrict the size
+of the build tree in order to speed up the rebuild time. A second use for
+build pruning is to actively prevent the recompilation of certain files that
+you know will recompile due to, for example, a modified header file. You may
+know that either the changes to the header file are immaterial, or that the
+changes may be safely ignored for most of the tree, for testing
+purposes.With Cons, the view is that it is pragmatic to admit this type of
+behavior, with the understanding that on the next full build everything that
+needs to be rebuilt will be. There is no equivalent to a ``make touch''
+command, to mark files as permanently up-to-date. So any risk that is
+incurred by build pruning is mitigated. For release quality work, obviously,
+we recommend that you do not use build pruning (it's perfectly OK to use
+during integration, however, for checking compilation, etc. Just be sure to
+do an unconstrained build before committing the integration).
+
+
+=head1 Temporary overrides
+
+Cons provides a very simple mechanism for overriding aspects of a build. The
+essence is that you write an override file containing one or more
+C<Override> commands, and you specify this on the command line, when you run
+C<cons>:
+
+ % cons -o over export
+
+will build the F<export> directory, with all derived files subject to the
+overrides present in the F<over> file. If you leave out the C<-o> option,
+then everything necessary to remove all overrides will be rebuilt.
+
+
+=head2 Overriding environment variables
+
+The override file can contain two types of overrides. The first is incoming
+environment variables. These are normally accessible by the F<Construct>
+file from the C<%ENV> hash variable. These can trivially be overridden in
+the override file by setting the appropriate elements of C<%ENV> (these
+could also be overridden in the user's environment, of course).
+
+
+=head2 The Override command
+
+The second type of override is accomplished with the C<Override> command,
+which looks like this:
+
+ Override <regexp>, <var1> => <value1>, <var2> => <value2>, ...;
+
+The regular expression I<regexp> is matched against every derived file that
+is a candidate for the build. If the derived file matches, then the
+variable/value pairs are used to override the values in the construction
+environment associated with the derived file.
+
+Let's suppose that we have a construction environment like this:
+
+ $CONS = new cons(
+ COPT => '',
+ CDBG => '-g',
+ CFLAGS => '%COPT %CDBG',
+ );
+
+Then if we have an override file F<over> containing this command:
+
+ Override '\.o$', COPT => '-O', CDBG => '';
+
+then any C<cons> invocation with C<-o over> that creates F<.o> files via
+this environment will cause them to be compiled with C<-O >and no C<-g>. The
+override could, of course, be restricted to a single directory by the
+appropriate selection of a regular expression.
+
+Here's the original version of the Hello, World! program, built with this
+environment. Note that Cons rebuilds the appropriate pieces when the
+override is applied or removed:
+
+ % cons hello
+ cc -g -c hello.c -o hello.o
+ cc -o hello hello.o
+ % cons -o over hello
+ cc -O -c hello.c -o hello.o
+ cc -o hello hello.o
+ % cons -o over hello
+ cons: "hello" is up-to-date.
+ % cons hello
+ cc -g -c hello.c -o hello.o
+ cc -o hello hello.o
+
+It's important that the C<Override> command only be used for temporary,
+on-the-fly overrides necessary for development because the overrides are not
+platform independent and because they rely too much on intimate knowledge of
+the workings of the scripts. For temporary use, however, they are exactly
+what you want.
+
+Note that it is still useful to provide, say, the ability to create a fully
+optimized version of a system for production use--from the F<Construct> and
+F<Conscript> files. This way you can tailor the optimized system to the
+platform. Where optimizer trade-offs need to be made (particular files may
+not be compiled with full optimization, for example), then these can be
+recorded for posterity (and reproducibility) directly in the scripts.
+
+
+=head1 More on construction environments
+
+As previously mentioned, a B<construction environment> is an object that
+has a set of keyword/value pairs and a set of methods, and which is used
+to tell Cons how target files should be built. This section describes
+how Cons uses and expands construction environment values to control its
+build behavior.
+
+=head2 Construction variable expansion
+
+Construction variables from a construction environment are expanded
+by preceding the keyword with a C<%> (percent sign):
+
+ Construction variables:
+ XYZZY => 'abracadabra',
+
+ The string: "The magic word is: %XYZZY!"
+ expands to: "The magic word is: abracadabra!"
+
+A construction variable name may be surrounded by C<{> and C<}> (curly
+braces), which are stripped as part of the expansion. This can
+sometimes be necessary to separate a variable expansion from trailing
+alphanumeric characters:
+
+ Construction variables:
+ OPT => 'value1',
+ OPTION => 'value2',
+
+ The string: "%OPT %{OPT}ION %OPTION %{OPTION}"
+ expands to: "value1 value1ION value2 value2"
+
+Construction variable expansion is recursive--that is, a string
+containing C<%->expansions after substitution will be re-expanded until
+no further substitutions can be made:
+
+ Construction variables:
+ STRING => 'The result is: %FOO',
+ FOO => '%BAR',
+ BAR => 'final value',
+
+ The string: "The string says: %STRING"
+ expands to: "The string says: The result is: final value"
+
+If a construction variable is not defined in an environment, then the
+null string is substituted:
+
+ Construction variables:
+ FOO => 'value1',
+ BAR => 'value2',
+
+ The string: "%FOO <%NO_VARIABLE> %BAR"
+ expands to: "value1 <> value2"
+
+A doubled C<%%> will be replaced by a single C<%>:
+
+ The string: "Here is a percent sign: %%"
+ expands to: "Here is a percent sign: %"
+
+=head2 Default construction variables
+
+When you specify no arguments when creating a new construction
+environment:
+
+ $env = new cons();
+
+Cons creates a reference to a new, default construction
+environment. This contains a number of construction variables and some
+methods. At the present writing, the default construction variables on a
+UNIX system are:
+
+ CC => 'cc',
+ CFLAGS => '',
+ CCCOM => '%CC %CFLAGS %_IFLAGS -c %< -o %>',
+ CXX => '%CC',
+ CXXFLAGS => '%CFLAGS',
+ CXXCOM => '%CXX %CXXFLAGS %_IFLAGS -c %< -o %>',
+ INCDIRPREFIX => '-I',
+ INCDIRSUFFIX => '',
+ LINK => '%CXX',
+ LINKCOM => '%LINK %LDFLAGS -o %> %< %_LDIRS %LIBS',
+ LINKMODULECOM => '%LD -r -o %> %<',
+ LIBDIRPREFIX => '-L',
+ LIBDIRSUFFIX => '',
+ AR => 'ar',
+ ARFLAGS => 'r',
+ ARCOM => ['%AR %ARFLAGS %> %<', '%RANLIB %>'],
+ RANLIB => 'ranlib',
+ AS => 'as',
+ ASFLAGS => '',
+ ASCOM => '%AS %ASFLAGS %< -o %>',
+ LD => 'ld',
+ LDFLAGS => '',
+ PREFLIB => 'lib',
+ SUFLIB => '.a',
+ SUFLIBS => '.so:.a',
+ SUFOBJ => '.o',
+ SIGNATURE => [ '*' => 'build' ],
+ ENV => { 'PATH' => '/bin:/usr/bin' },
+
+
+And on a Win32 system (Windows NT), the default construction variables
+are (unless the default rule style is set using the B<DefaultRules>
+method):
+
+ CC => 'cl',
+ CFLAGS => '/nologo',
+ CCCOM => '%CC %CFLAGS %_IFLAGS /c %< /Fo%>',
+ CXXCOM => '%CXX %CXXFLAGS %_IFLAGS /c %< /Fo%>',
+ INCDIRPREFIX => '/I',
+ INCDIRSUFFIX => '',
+ LINK => 'link',
+ LINKCOM => '%LINK %LDFLAGS /out:%> %< %_LDIRS %LIBS',
+ LINKMODULECOM => '%LD /r /o %> %<',
+ LIBDIRPREFIX => '/LIBPATH:',
+ LIBDIRSUFFIX => '',
+ AR => 'lib',
+ ARFLAGS => '/nologo ',
+ ARCOM => "%AR %ARFLAGS /out:%> %<",
+ RANLIB => '',
+ LD => 'link',
+ LDFLAGS => '/nologo ',
+ PREFLIB => '',
+ SUFEXE => '.exe',
+ SUFLIB => '.lib',
+ SUFLIBS => '.dll:.lib',
+ SUFOBJ => '.obj',
+ SIGNATURE => [ '*' => 'build' ],
+
+These variables are used by the various methods associated with the
+environment. In particular, any method that ultimately invokes an external
+command will substitute these variables into the final command, as
+appropriate. For example, the C<Objects> method takes a number of source
+files and arranges to derive, if necessary, the corresponding object
+files:
+
+ Objects $env 'foo.c', 'bar.c';
+
+This will arrange to produce, if necessary, F<foo.o> and F<bar.o>. The
+command invoked is simply C<%CCCOM>, which expands, through substitution,
+to the appropriate external command required to build each object. The
+substitution rules will be discussed in detail in the next section.
+
+The construction variables are also used for other purposes. For example,
+C<CPPPATH> is used to specify a colon-separated path of include
+directories. These are intended to be passed to the C preprocessor and are
+also used by the C-file scanning machinery to determine the dependencies
+involved in a C Compilation.
+
+Variables beginning with underscore are created by various methods,
+and should normally be considered ``internal'' variables. For example,
+when a method is called which calls for the creation of an object from
+a C source, the variable C<_IFLAGS> is created: this corresponds to the
+C<-I> switches required by the C compiler to represent the directories
+specified by C<CPPPATH>.
+
+Note that, for any particular environment, the value of a variable is set
+once, and then never reset (to change a variable, you must create a new
+environment. Methods are provided for copying existing environments for this
+purpose). Some internal variables, such as C<_IFLAGS> are created on demand,
+but once set, they remain fixed for the life of the environment.
+
+The C<CFLAGS>, C<LDFLAGS>, and C<ARFLAGS> variables all supply a place
+for passing options to the compiler, loader, and archiver, respectively.
+
+The C<INCDIRPREFIX> and C<INCDIRSUFFIX> variables specify option
+strings to be appended to the beginning and end, respectively, of each
+include directory so that the compiler knows where to find F<.h> files.
+Similarly, the C<LIBDIRPREFIX> and C<LIBDIRSUFFIX> variables specify the
+option string to be appended to the beginning of and end, respectively,
+of each directory that the linker should search for libraries.
+
+Another variable, C<ENV>, is used to determine the system environment during
+the execution of an external command. By default, the only environment
+variable that is set is C<PATH>, which is the execution path for a UNIX
+command. For the utmost reproducibility, you should really arrange to set
+your own execution path, in your top-level F<Construct> file (or perhaps by
+importing an appropriate construction package with the Perl C<use>
+command). The default variables are intended to get you off the ground.
+
+=head2 Expanding variables in construction commands
+
+Within a construction command, construction variables will be expanded
+according to the rules described above. In addition to normal variable
+expansion from the construction environment, construction commands also
+expand the following pseudo-variables to insert the specific input and
+output files in the command line that will be executed:
+
+=over 10
+
+=item %>
+
+The target file name. In a multi-target command, this expands to the
+first target mentioned.)
+
+=item %0
+
+Same as C<%E<gt>>.
+
+=item %1, %2, ..., %9
+
+These refer to the first through ninth input file, respectively.
+
+=item %E<lt>
+
+The full set of input file names. If any of these have been used
+anywhere else in the current command line (via C<%1>, C<%2>, etc.), then
+those will be deleted from the list provided by C<%E<lt>>. Consider the
+following command found in a F<Conscript> file in the F<test> directory:
+
+ Command $env 'tgt', qw(foo bar baz), qq(
+ echo %< -i %1 > %>
+ echo %< -i %2 >> %>
+ echo %< -i %3 >> %>
+ );
+
+If F<tgt> needed to be updated, then this would result in the execution of
+the following commands, assuming that no remapping has been established for
+the F<test> directory:
+
+ echo test/bar test/baz -i test/foo > test/tgt
+ echo test/foo test/baz -i test/bar >> test/tgt
+ echo test/foo test/bar -i test/baz >> test/tgt
+
+=back
+
+Any of the above pseudo-variables may be followed immediately by one of
+the following suffixes to select a portion of the expanded path name:
+
+ :a the absolute path to the file name
+ :b the directory plus the file name stripped of any suffix
+ :d the directory
+ :f the file name
+ :s the file name suffix
+ :F the file name stripped of any suffix
+ :S the absolute path path to a Linked source file
+
+Continuing with the above example, C<%E<lt>:f> would expand to C<foo bar baz>,
+and C<%E<gt>:d> would expand to C<test>.
+
+There are additional C<%> elements which affect the command line(s):
+
+=over 10
+
+=item %[ %]
+
+It is possible to programmatically rewrite part of the command by
+enclosing part of it between C<%[> and C<%]>. This will call the
+construction variable named as the first word enclosed in the brackets
+as a Perl code reference; the results of this call will be used to
+replace the contents of the brackets in the command line. For example,
+given an existing input file named F<tgt.in>:
+
+ @keywords = qw(foo bar baz);
+ $env = new cons(X_COMMA => sub { join(",", @_) });
+ Command $env 'tgt', 'tgt.in', qq(
+ echo '# Keywords: %[X_COMMA @keywords %]' > %>
+ cat %< >> %>
+ );
+
+This will execute:
+
+ echo '# Keywords: foo,bar,baz' > tgt
+ cat tgt.in >> tgt
+
+=item %( %)
+
+Cons includes the text of the command line in the MD5 signature for a
+build, so that targets get rebuilt if you change the command line (to
+add or remove an option, for example). Command-line text in between
+C<%(> and C<%)>, however, will be ignored for MD5 signature calculation.
+
+Internally, Cons uses C<%(> and C<%)> around include and library
+directory options (C<-I> and C<-L> on UNIX systems, C</I> and
+C</LIBPATH> on Windows NT) to avoid rebuilds just because the directory
+list changes. Rebuilds occur only if the changed directory list causes
+any included I<files> to change, and a changed include file is detected
+by the MD5 signature calculation on the actual file contents.
+
+=back
+
+=head2 Expanding construction variables in file names
+
+Cons expands construction variables in the source and target file names
+passed to the various construction methods according to the expansion
+rules described above:
+
+ $env = new cons(
+ DESTDIR => 'programs',
+ SRCDIR => 'src',
+ );
+ Program $env '%DESTDIR/hello', '%SRCDIR/hello.c';
+
+This allows for flexible configuration, through the construction
+environment, of directory names, suffixes, etc.
+
+
+=head1 Build actions
+
+Cons supports several types of B<build actions> that can be performed
+to construct one or more target files. Usually, a build action is
+a construction command--that is, a command-line string that invokes
+an external command. Cons can also execute Perl code embedded in a
+command-line string, and even supports an experimental ability to build
+a target file by executing a Perl code reference directly.
+
+A build action is usually specified as the value of a construction
+variable:
+
+ $env = new cons(
+ CCCOM => '%CC %CFLAGS %_IFLAGS -c %< -o %>',
+ LINKCOM => '[perl] &link_executable("%>", "%<")',
+ ARCOM => sub { my($env, $target, @sources) = @_;
+ # code to create an archive
+ }
+ );
+
+A build action may be associated directly with one or more target files
+via the C<Command> method; see below.
+
+=head2 Construction commands
+
+A construction command goes through expansion of construction variables
+and C<%-> pseudo-variables, as described above, to create the actual
+command line that Cons will execute to generate the target file or
+files.
+
+After substitution occurs, strings of white space are converted into
+single blanks, and leading and trailing white space is eliminated. It
+is therefore currently not possible to introduce variable length white
+space in strings passed into a command.
+
+If a multi-line command string is provided, the commands are executed
+sequentially. If any of the commands fails, then none of the rest are
+executed, and the target is not marked as updated, i.e. a new signature is
+not stored for the target.
+
+Normally, if all the commands succeed, and return a zero status (or whatever
+platform-specific indication of success is required), then a new signature
+is stored for the target. If a command erroneously reports success even
+after a failure, then Cons will assume that the target file created by that
+command is accurate and up-to-date.
+
+The first word of each command string, after expansion, is assumed to be an
+executable command looked up on the C<PATH> environment variable (which is,
+in turn, specified by the C<ENV> construction variable). If this command is
+found on the path, then the target will depend upon it: the command will
+therefore be automatically built, as necessary. It's possible to write
+multi-part commands to some shells, separated by semi-colons. Only the first
+command word will be depended upon, however, so if you write your command
+strings this way, you must either explicitly set up a dependency (with the
+C<Depends> method), or be sure that the command you are using is a system
+command which is expected to be available. If it isn't available, you will,
+of course, get an error.
+
+Cons normally prints a command before executing it. This behavior is
+suppressed if the first character of the command is C<@>. Note that
+you may need to separate the C<@> from the command name or escape it to
+prevent C<@cmd> from looking like an array to Perl quote operators that
+perform interpolation:
+
+ # The first command line is incorrect,
+ # because "@cp" looks like an array
+ # to the Perl qq// function.
+ # Use the second form instead.
+ Command $env 'foo', 'foo.in', qq(
+ @cp %< tempfile
+ @ cp tempfile %>
+ );
+
+If there are shell meta characters anywhere in the expanded command line,
+such as C<E<lt>>, C<E<gt>>, quotes, or semi-colon, then the command
+will actually be executed by invoking a shell. This means that a command
+such as:
+
+ cd foo
+
+alone will typically fail, since there is no command C<cd> on the path. But
+the command string:
+
+ cd $<:d; tar cf $>:f $<:f
+
+when expanded will still contain the shell meta character semi-colon, and a
+shell will be invoked to interpret the command. Since C<cd> is interpreted
+by this sub-shell, the command will execute as expected.
+
+=head2 Perl expressions
+
+If any command (even one within a multi-line command) begins with
+C<[perl]>, the remainder of that command line will be evaluated by the
+running Perl instead of being forked by the shell. If an error occurs
+in parsing the Perl code, or if the Perl expression returns 0 or undef,
+the command will be considered to have failed. For example, here is a
+simple command which creates a file C<foo> directly from Perl:
+
+ $env = new cons();
+ Command $env 'foo',
+ qq([perl] open(FOO,'>foo');print FOO "hi\\n"; close(FOO); 1);
+
+Note that when the command is executed, you are in the same package as
+when the F<Construct> or F<Conscript> file was read, so you can call
+Perl functions you've defined in the same F<Construct> or F<Conscript>
+file in which the C<Command> appears:
+
+ $env = new cons();
+ sub create_file {
+ my $file = shift;
+ open(FILE, ">$file");
+ print FILE "hi\n";
+ close(FILE);
+ return 1;
+ }
+ Command $env 'foo', "[perl] &create_file('%>')";
+
+The Perl string will be used to generate the signature for the derived
+file, so if you change the string, the file will be rebuilt. The contents
+of any subroutines you call, however, are not part of the signature,
+so if you modify a called subroutine such as C<create_file> above,
+the target will I<not> be rebuilt. Caveat user.
+
+=head2 Perl code references [EXPERIMENTAL]
+
+Cons supports the ability to create a derived file by directly executing
+a Perl code reference. This feature is considered EXPERIMENTAL and
+subject to change in the future.
+
+A code reference may either be a named subroutine referenced by the
+usual C<\&> syntax:
+
+ sub build_output {
+ my($env, $target, @sources) = @_;
+ print "build_output building $target\n";
+ open(OUT, ">$target");
+ foreach $src (@sources) {
+ if (! open(IN, "<$src")) {
+ print STDERR "cannot open '$src': $!\n";
+ return undef;
+ }
+ print OUT, <IN>;
+ }
+ close(OUT);
+ return 1;
+ }
+ Command $env 'output', \&build_output;
+
+or the code reference may be an anonymous subroutine:
+
+ Command $env 'output', sub {
+ my($env, $target, @sources) = @_;
+ print "building $target\n";
+ open(FILE, ">$target");
+ print FILE "hello\n";
+ close(FILE);
+ return 1;
+ };
+
+To build the target file, the referenced subroutine is passed, in order:
+the construction environment used to generate the target; the path
+name of the target itself; and the path names of all the source files
+necessary to build the target file.
+
+The code reference is expected to generate the target file, of course,
+but may manipulate the source and target files in any way it chooses.
+The code reference must return a false value (C<undef> or C<0>) if
+the build of the file failed. Any true value indicates a successful
+build of the target.
+
+Building target files using code references is considered EXPERIMENTAL
+due to the following current limitations:
+
+=over 4
+
+Cons does I<not> print anything to indicate the code reference is being
+called to build the file. The only way to give the user any indication
+is to have the code reference explicitly print some sort of "building"
+message, as in the above examples.
+
+Cons does not generate any signatures for code references, so if the
+code in the reference changes, the target will I<not> be rebuilt.
+
+Cons has no public method to allow a code reference to extract
+construction variables. This would be good to allow generalization of
+code references based on the current construction environment, but would
+also complicate the problem of generating meaningful signatures for code
+references.
+
+=back
+
+Support for building targets via code references has been released in
+this version to encourage experimentation and the seeking of possible
+solutions to the above limitations.
+
+
+=head1 Default construction methods
+
+The list of default construction methods includes the following:
+
+
+=head2 The C<new> constructor
+
+The C<new> method is a Perl object constructor. That is, it is not invoked
+via a reference to an existing construction environment B<reference>, but,
+rather statically, using the name of the Perl B<package> where the
+constructor is defined. The method is invoked like this:
+
+ $env = new cons(<overrides>);
+
+The environment you get back is blessed into the package C<cons>, which
+means that it will have associated with it the default methods described
+below. Individual construction variables can be overridden by providing
+name/value pairs in an override list. Note that to override any command
+environment variable (i.e. anything under C<ENV>), you will have to override
+all of them. You can get around this difficulty by using the C<copy> method
+on an existing construction environment.
+
+
+=head2 The C<clone> method
+
+The C<clone> method creates a clone of an existing construction environment,
+and can be called as in the following example:
+
+ $env2 = $env1->clone(<overrides>);
+
+You can provide overrides in the usual manner to create a different
+environment from the original. If you just want a new name for the same
+environment (which may be helpful when exporting environments to existing
+components), you can just use simple assignment.
+
+
+=head2 The C<copy> method
+
+The C<copy> method extracts the externally defined construction variables
+from an environment and returns them as a list of name/value
+pairs. Overrides can also be provided, in which case, the overridden values
+will be returned, as appropriate. The returned list can be assigned to a
+hash, as shown in the prototype, below, but it can also be manipulated in
+other ways:
+
+ %env = $env1->copy(<overrides>);
+
+The value of C<ENV>, which is itself a hash, is also copied to a new hash,
+so this may be changed without fear of affecting the original
+environment. So, for example, if you really want to override just the
+C<PATH> variable in the default environment, you could do the following:
+
+ %cons = new cons()->copy();
+ $cons{ENV}{PATH} = "<your path here>";
+ $cons = new cons(%cons);
+
+This will leave anything else that might be in the default execution
+environment undisturbed.
+
+
+=head2 The C<Install> method
+
+The C<Install> method arranges for the specified files to be installed in
+the specified directory. The installation is optimized: the file is not
+copied if it can be linked. If this is not the desired behavior, you will
+need to use a different method to install the file. It is called as follows:
+
+ Install $env <directory>, <names>;
+
+Note that, while the files to be installed may be arbitrarily named,
+only the last component of each name is used for the installed target
+name. So, for example, if you arrange to install F<foo/bar> in F<baz>,
+this will create a F<bar> file in the F<baz> directory (not F<foo/bar>).
+
+
+=head2 The C<InstallAs> method
+
+The C<InstallAs> method arranges for the specified source file(s) to be
+installed as the specified target file(s). Multiple files should be
+specified as a file list. The installation is optimized: the file is not
+copied if it can be linked. If this is not the desired behavior, you will
+need to use a different method to install the file. It is called as follows:
+
+C<InstallAs> works in two ways:
+
+Single file install:
+
+ InstallAs $env TgtFile, SrcFile;
+
+Multiple file install:
+
+ InstallAs $env ['tgt1', 'tgt2'], ['src1', 'src2'];
+
+Or, even as:
+
+ @srcs = qw(src1 src2 src3);
+ @tgts = qw(tgt1 tgt2 tgt3);
+ InstallAs $env [@tgts], [@srcs];
+
+Both the target and the sources lists should be of the same length.
+
+=head2 The C<Precious> method
+
+The C<Precious> method asks cons not to delete the specified file or
+list of files before building them again. It is invoked as:
+
+ Precious <files>;
+
+This is especially useful for allowing incremental updates to libraries
+or debug information files which are updated rather than rebuilt anew each
+time. Cons will still delete the files when the C<-r> flag is specified.
+
+=head2 The C<AfterBuild> method
+
+The C<AfterBuild> method evaluates the specified perl string after
+building the given file or files (or finding that they are up to date).
+The eval will happen once per specified file. C<AfterBuild> is called
+as follows:
+
+ AfterBuild $env 'foo.o', qq(print "foo.o is up to date!\n");
+
+The perl string is evaluated in the C<script> package, and has access
+to all variables and subroutines defined in the F<Conscript> file in
+which the C<AfterBuild> method is called.
+
+=head2 The C<Command> method
+
+The C<Command> method is a catchall method which can be used to arrange for
+any build action to be executed to update the target. For this command, a
+target file and list of inputs is provided. In addition, a build action
+is specified as the last argument. The build action is typically a
+command line or lines, but may also contain Perl code to be executed;
+see the section above on build actions for details.
+
+The C<Command> method is called as follows:
+
+ Command $env <target>, <inputs>, <build action>;
+
+The target is made dependent upon the list of input files specified, and the
+inputs must be built successfully or Cons will not attempt to build the
+target.
+
+To specify a command with multiple targets, you can specify a reference to a
+list of targets. In Perl, a list reference can be created by enclosing a
+list in square brackets. Hence the following command:
+
+ Command $env ['foo.h', 'foo.c'], 'foo.template', q(
+ gen %1
+ );
+
+could be used in a case where the command C<gen> creates two files, both
+F<foo.h> and F<foo.c>.
+
+
+=head2 The C<Objects> method
+
+The C<Objects> method arranges to create the object files that correspond to
+the specified source files. It is invoked as shown below:
+
+ @files = Objects $env <source or object files>;
+
+Under Unix, source files ending in F<.s> and F<.c> are currently
+supported, and will be compiled into a name of the same file ending
+in F<.o>. By default, all files are created by invoking the external
+command which results from expanding the C<CCCOM> construction variable,
+with C<%E<lt>> and C<%E<gt>> set to the source and object files,
+respectively. (See the section above on construction variable expansion
+for details). The variable C<CPPPATH> is also used when scanning source
+files for dependencies. This is a colon separated list of pathnames, and
+is also used to create the construction variable C<_IFLAGS,> which will
+contain the appropriate list of -C<I> options for the compilation. Any
+relative pathnames in C<CPPPATH> is interpreted relative to the
+directory in which the associated construction environment was created
+(absolute and top-relative names may also be used). This variable is
+used by C<CCCOM>. The behavior of this command can be modified by
+changing any of the variables which are interpolated into C<CCCOM>, such
+as C<CC>, C<CFLAGS>, and, indirectly, C<CPPPATH>. It's also possible
+to replace the value of C<CCCOM>, itself. As a convenience, this file
+returns the list of object filenames.
+
+
+=head2 The C<Program> method
+
+The C<Program> method arranges to link the specified program with the
+specified object files. It is invoked in the following manner:
+
+ Program $env <program name>, <source or object files>;
+
+The program name will have the value of the C<SUFEXE> construction
+variable appended (by default, C<.exe> on Win32 systems, nothing on Unix
+systems) if the suffix is not already present.
+
+Source files may be specified in place of objects files--the C<Objects>
+method will be invoked to arrange the conversion of all the files into
+object files, and hence all the observations about the C<Objects> method,
+above, apply to this method also.
+
+The actual linking of the program will be handled by an external command
+which results from expanding the C<LINKCOM> construction variable, with
+C<%E<lt>> set to the object files to be linked (in the order presented),
+and C<%E<gt>> set to the target. (See the section above on construction
+variable expansion for details.) The user may set additional variables
+in the construction environment, including C<LINK>, to define which
+program to use for linking, C<LIBPATH>, a colon-separated list of
+library search paths, for use with library specifications of the form
+I<-llib>, and C<LIBS>, specifying the list of libraries to link against
+(in either I<-llib> form or just as pathnames. Relative pathnames in
+both C<LIBPATH> and C<LIBS> are interpreted relative to the directory
+in which the associated construction environment is created (absolute
+and top-relative names may also be used). Cons automatically sets up
+dependencies on any libraries mentioned in C<LIBS>: those libraries will
+be built before the command is linked.
+
+
+=head2 The C<Library> method
+
+The C<Library> method arranges to create the specified library from the
+specified object files. It is invoked as follows:
+
+ Library $env <library name>, <source or object files>;
+
+The library name will have the value of the C<SUFLIB> construction
+variable appended (by default, C<.lib> on Win32 systems, C<.a> on Unix
+systems) if the suffix is not already present.
+
+Source files may be specified in place of objects files--the C<Objects>
+method will be invoked to arrange the conversion of all the files into
+object files, and hence all the observations about the C<Objects> method,
+above, apply to this method also.
+
+The actual creation of the library will be handled by an external
+command which results from expanding the C<ARCOM> construction variable,
+with C<%E<lt>> set to the library members (in the order presented),
+and C<%E<gt>> to the library to be created. (See the section above
+on construction variable expansion for details.) The user may set
+variables in the construction environment which will affect the
+operation of the command. These include C<AR>, the archive program
+to use, C<ARFLAGS>, which can be used to modify the flags given to
+the program specified by C<AR>, and C<RANLIB>, the name of a archive
+index generation program, if needed (if the particular need does not
+require the latter functionality, then C<ARCOM> must be redefined to not
+reference C<RANLIB>).
+
+The C<Library> method allows the same library to be specified in multiple
+method invocations. All of the contributing objects from all the invocations
+(which may be from different directories) are combined and generated by a
+single archive command. Note, however, that if you prune a build so that
+only part of a library is specified, then only that part of the library will
+be generated (the rest will disappear!).
+
+
+=head2 The C<Module> method
+
+The C<Module> method is a combination of the C<Program> and C<Command>
+methods. Rather than generating an executable program directly, this command
+allows you to specify your own command to actually generate a module. The
+method is invoked as follows:
+
+ Module $env <module name>, <source or object files>, <construction command>;
+
+This command is useful in instances where you wish to create, for example,
+dynamically loaded modules, or statically linked code libraries.
+
+
+=head2 The C<Depends> method
+
+The C<Depends> method allows you to specify additional dependencies for a
+target. It is invoked as follows:
+
+ Depends $env <target>, <dependencies>;
+
+This may be occasionally useful, especially in cases where no scanner exists
+(or is writable) for particular types of files. Normally, dependencies are
+calculated automatically from a combination of the explicit dependencies set
+up by the method invocation or by scanning source files.
+
+A set of identical dependencies for multiple targets may be specified
+using a reference to a list of targets. In Perl, a list reference can
+be created by enclosing a list in square brackets. Hence the following
+command:
+
+ Depends $env ['foo', 'bar'], 'input_file_1', 'input_file_2';
+
+specifies that both the F<foo> and F<bar> files depend on the listed
+input files.
+
+
+=head2 The C<RuleSet> method
+
+The C<RuleSet> method returns the construction variables for building
+various components with one of the rule sets supported by Cons. The
+currently supported rule sets are:
+
+=over 4
+
+=item msvc
+
+Rules for the Microsoft Visual C++ compiler suite.
+
+=item unix
+
+Generic rules for most UNIX-like compiler suites.
+
+=back
+
+On systems with more than one available compiler suite, this allows you
+to easily create side-by-side environments for building software with
+multiple tools:
+
+ $msvcenv = new cons(RuleSet("msvc"));
+ $cygnusenv = new cons(RuleSet("unix"));
+
+In the future, this could also be extended to other platforms that
+have different default rule sets.
+
+
+=head2 The C<DefaultRules> method
+
+The C<DefaultRules> method sets the default construction variables that
+will be returned by the C<new> method to the specified arguments:
+
+ DefaultRules(CC => 'gcc',
+ CFLAGS => '',
+ CCCOM => '%CC %CFLAGS %_IFLAGS -c %< -o %>');
+ $env = new cons();
+ # $env now contains *only* the CC, CFLAGS,
+ # and CCCOM construction variables
+
+Combined with the C<RuleSet> method, this also provides an easy way
+to set explicitly the default build environment to use some supported
+toolset other than the Cons defaults:
+
+ # use a UNIX-like tool suite (like cygwin) on Win32
+ DefaultRules(RuleSet('unix'));
+ $env = new cons();
+
+Note that the C<DefaultRules> method completely replaces the default
+construction environment with the specified arguments, it does not
+simply override the existing defaults. To override one or more
+variables in a supported C<RuleSet>, append the variables and values:
+
+ DefaultRules(RuleSet('unix'), CFLAGS => '-O3');
+ $env1 = new cons();
+ $env2 = new cons();
+ # both $env1 and $env2 have 'unix' defaults
+ # with CFLAGS set to '-O3'
+
+
+=head2 The C<Ignore> method
+
+The C<Ignore> method allows you to ignore explicitly dependencies that
+Cons infers on its own. It is invoked as follows:
+
+ Ignore <patterns>;
+
+This can be used to avoid recompilations due to changes in system header
+files or utilities that are known to not affect the generated targets.
+
+If, for example, a program is built in an NFS-mounted directory on
+multiple systems that have different copies of F<stdio.h>, the differences
+will affect the signatures of all derived targets built from source files
+that C<#include E<lt>stdio.hE<gt>>. This will cause all those targets to
+be rebuilt when changing systems. If this is not desirable behavior, then
+the following line will remove the dependencies on the F<stdio.h> file:
+
+ Ignore '^/usr/include/stdio\.h$';
+
+Note that the arguments to the C<Ignore> method are regular expressions,
+so special characters must be escaped and you may wish to anchor the
+beginning or end of the expression with C<^> or C<$> characters.
+
+
+=head2 The C<Salt> method
+
+The C<Salt> method adds a constant value to the signature calculation
+for every derived file. It is invoked as follows:
+
+ Salt $string;
+
+Changing the Salt value will force a complete rebuild of every derived
+file. This can be used to force rebuilds in certain desired
+circumstances. For example,
+
+ Salt `uname -s`;
+
+Would force a complete rebuild of every derived file whenever the
+operating system on which the build is performed (as reported by C<uname
+-s>) changes.
+
+
+=head2 The C<UseCache> method
+
+The C<UseCache> method instructs Cons to maintain a cache of derived
+files, to be shared among separate build trees of the same project.
+
+ UseCache("cache/<buildname>") || warn("cache directory not found");
+
+
+=head2 The C<SourcePath> method
+
+The C<SourcePath> mathod returns the real source path name of a file,
+as opposed to the path name within a build directory. It is invoked
+as follows:
+
+ $path = SourcePath <buildpath>;
+
+
+=head2 The C<ConsPath> method
+
+The C<ConsPath> method returns true if the supplied path is a derivable
+file, and returns undef (false) otherwise.
+It is invoked as follows:
+
+ $result = ConsPath <path>;
+
+
+=head2 The C<SplitPath> method
+
+The C<SplitPath> method looks up multiple path names in a string separated
+by the default path separator for the operating system (':' on UNIX
+systems, ';' on Windows NT), and returns the fully-qualified names.
+It is invoked as follows:
+
+ @paths = SplitPath <pathlist>;
+
+The C<SplitPath> method will convert names prefixed '#' to the
+appropriate top-level build name (without the '#') and will convert
+relative names to top-level names.
+
+
+=head2 The C<DirPath> method
+
+The C<DirPath> method returns the build path name(s) of a directory or
+list of directories. It is invoked as follows:
+
+ $cwd = DirPath <paths>;
+
+The most common use for the C<DirPath> method is:
+
+ $cwd = DirPath '.';
+
+to fetch the path to the current directory of a subsidiary F<Conscript>
+file.
+
+
+=head2 The C<FilePath> method
+
+The C<FilePath> method returns the build path name(s) of a file or
+list of files. It is invoked as follows:
+
+ $file = FilePath <path>;
+
+
+=head2 The C<Help> method
+
+The C<Help> method specifies help text that will be displayed when the
+user invokes C<cons -h>. This can be used to provide documentation
+of specific targets, values, build options, etc. for the build tree.
+It is invoked as follows:
+
+ Help <helptext>;
+
+The C<Help> method may only be called once, and should typically be
+specified in the top-level F<Construct> file.
+
+
+=head1 Extending Cons
+
+
+=head2 Overriding construction variables
+
+There are several ways of extending Cons, which vary in degree of
+difficulty. The simplest method is to define your own construction
+environment, based on the default environment, but modified to reflect your
+particular needs. This will often suffice for C-based applications. You can
+use the C<new> constructor, and the C<clone> and C<copy> methods to create
+hybrid environments. These changes can be entirely transparent to the
+underlying F<Conscript> files.
+
+
+=head2 Adding new methods
+
+For slightly more demanding changes, you may wish to add new methods to the
+C<cons> package. Here's an example of a very simple extension,
+C<InstallScript>, which installs a tcl script in a requested location, but
+edits the script first to reflect a platform-dependent path that needs to be
+installed in the script:
+
+ # cons::InstallScript - Create a platform dependent version of a shell
+ # script by replacing string ``#!your-path-here'' with platform specific
+ # path $BIN_DIR.
+
+ sub cons::InstallScript {
+ my ($env, $dst, $src) = @_;
+ Command $env $dst, $src, qq(
+ sed s+your-path-here+$BIN_DIR+ %< > %>
+ chmod oug+x %>
+ );
+ }
+
+Notice that this method is defined directly in the C<cons> package (by
+prefixing the name with C<cons::>). A change made in this manner will be
+globally visible to all environments, and could be called as in the
+following example:
+
+ InstallScript $env "$BIN/foo", "foo.tcl";
+
+For a small improvement in generality, the C<BINDIR> variable could be
+passed in as an argument or taken from the construction environment--as
+C<%BINDIR>.
+
+
+=head2 Overriding methods
+
+Instead of adding the method to the C<cons> name space, you could define a
+new package which inherits existing methods from the C<cons> package and
+overrides or adds others. This can be done using Perl's inheritance
+mechanisms.
+
+The following example defines a new package C<cons::switch> which
+overrides the standard C<Library> method. The overridden method builds
+linked library modules, rather than library archives. A new
+constructor is provided. Environments created with this constructor
+will have the new library method; others won't.
+
+ package cons::switch;
+ BEGIN {@ISA = 'cons'}
+
+ sub new {
+ shift;
+ bless new cons(@_);
+ }
+
+ sub Library {
+ my($env) = shift;
+ my($lib) = shift;
+ my(@objs) = Objects $env @_;
+ Command $env $lib, @objs, q(
+ %LD -r %LDFLAGS %< -o %>
+ );
+ }
+
+This functionality could be invoked as in the following example:
+
+ $env = new cons::switch(@overrides);
+ ...
+ Library $env 'lib.o', 'foo.c', 'bar.c';
+
+
+=head1 Invoking Cons
+
+The C<cons> command is usually invoked from the root of the build tree. A
+F<Construct> file must exist in that directory. If the C<-f> argument is
+used, then an alternate F<Construct> file may be used (and, possibly, an
+alternate root, since C<cons> will cd to F<Construct> file's containing
+directory).
+
+If C<cons> is invoked from a child of the root of the build tree with
+the C<-t> argument, it will walk up the directory hierarchy looking for a
+F<Construct> file. (An alternate name may still be specified with C<-f>.)
+The targets supplied on the command line will be modified to be relative
+to the discovered F<Construct> file. For example, from a directory
+containing a top-level F<Construct> file, the following invocation:
+
+ % cd libfoo/subdir
+ % cons -t target
+
+is exactly equivalent to:
+
+ % cons libfoo/subdir/target
+
+If there are any C<Default> targets specified in the directory hierarchy's
+F<Construct> or F<Conscript> files, only the default targets at or below
+the directory from which C<cons -t> was invoked will be built.
+
+The command is invoked as follows:
+
+ cons <arguments> -- <construct-args>
+
+where I<arguments> can be any of the following, in any order:
+
+=over 10
+
+=item I<target>
+
+Build the specified target. If I<target> is a directory, then recursively
+build everything within that directory.
+
+=item I<+pattern>
+
+Limit the F<Conscript> files considered to just those that match I<pattern>,
+which is a Perl regular expression. Multiple C<+> arguments are accepted.
+
+=item I<name>=<val>
+
+Sets I<name> to value I<val> in the C<ARG> hash passed to the top-level
+F<Construct> file.
+
+=item C<-cc>
+
+Show command that would have been executed, when retrieving from cache. No
+indication that the file has been retrieved is given; this is useful for
+generating build logs that can be compared with real build logs.
+
+=item C<-cd>
+
+Disable all caching. Do not retrieve from cache nor flush to cache.
+
+=item C<-cr>
+
+Build dependencies in random order. This is useful when building multiple
+similar trees with caching enabled.
+
+=item C<-cs>
+
+Synchronize existing build targets that are found to be up-to-date with
+cache. This is useful if caching has been disabled with -cc or just recently
+enabled with UseCache.
+
+=item C<-d>
+
+Enable dependency debugging.
+
+=item C<-f> <file>
+
+Use the specified file instead of F<Construct> (but first change to
+containing directory of I<file>).
+
+=item C<-h>
+
+Show a help message local to the current build if one such is defined, and
+exit.
+
+=item C<-k>
+
+Keep going as far as possible after errors.
+
+=item C<-o> <file>
+
+Read override file I<file>.
+
+=item C<-p>
+
+Show construction products in specified trees. No build is attempted.
+
+=item C<-pa>
+
+Show construction products and associated actions. No build is attempted.
+
+=item C<-pw>
+
+Show products and where they are defined. No build is attempted.
+
+=item C<-q>
+
+Make the build quiet. Multiple C<-q> options may be specified.
+
+A single C<-q> options suppress messages about Installing and Removing
+targets.
+
+Two C<-q> options suppress build command lines and target up-to-date
+messages.
+
+=item C<-r>
+
+Remove construction products associated with <targets>. No build is
+attempted.
+
+=item C<-R> <repos>
+
+Search for files in I<repos>. Multiple B<-R> I<repos> directories are
+searched in the order specified.
+
+=item C<-S> <pkg>
+
+Use the sig::<pkg> package to calculate. Supported <pkg> values
+include "md5" for MD5 signature calculation and "md5::debug" for debug
+information about MD5 signature calculation.
+
+If the specified package ends in <::debug>, signature debug information
+will be printed to the file name specified in the C<CONS_SIG_DEBUG>
+environment variable, or to standard output if the environment variable
+is not set.
+
+=item C<-t>
+
+Traverse up the directory hierarchy looking for a F<Construct> file,
+if none exists in the current directory. Targets will be modified to
+be relative to the F<Construct> file.
+
+Internally, C<cons> will change its working directory to the directory
+which contains the top-level F<Construct> file and report:
+
+ cons: Entering directory `top-level-directory'
+
+This message indicates to an invoking editor (such as emacs) or build
+environment that Cons will now report all file names relative to the
+top-level directory. This message can not be suppressed with the C<-q>
+option.
+
+=item C<-v>
+
+Show C<cons> version and continue processing.
+
+=item C<-V>
+
+Show C<cons> version and exit.
+
+=item C<-wf> <file>
+
+Write all filenames considered into I<file>.
+
+=item C<-x>
+
+Show a help message similar to this one, and exit.
+
+=back
+
+And I<construct-args> can be any arguments that you wish to process in the
+F<Construct> file. Note that there should be a B<--> separating the arguments
+to cons and the arguments that you wish to process in the F<Construct> file.
+
+Processing of I<construct-args> can be done by any standard package like
+B<Getopt> or its variants, or any user defined package. B<cons> will pass in
+the I<construct-args> as B<@ARGV> and will not attempt to interpret anything
+after the B<-->.
+
+ % cons -R /usr/local/repository -d os=solaris +driver -- -c test -f DEBUG
+
+would pass the following to cons
+
+ -R /usr/local/repository -d os=solaris +driver
+
+and the following, to the top level F<Construct> file as B<@ARGV>
+
+ -c test -f DEBUG
+
+Note that C<cons -r .> is equivalent to a full recursive C<make clean>,
+but requires no support in the F<Construct> file or any F<Conscript>
+files. This is most useful if you are compiling files into source
+directories (if you separate the F<build> and F<export> directories,
+then you can just remove the directories).
+
+The options C<-p>, C<-pa>, and C<-pw> are extremely useful for use as an aid
+in reading scripts or debugging them. If you want to know what script
+installs F<export/include/foo.h>, for example, just type:
+
+ % cons -pw export/include/foo.h
+
+
+=head1 Using and writing dependency scanners
+
+QuickScan allows simple target-independent scanners to be set up for
+source files. Only one QuickScan scanner may be associated with any given
+source file and environment, although the same scanner may (and should)
+be used for multiple files of a given type.
+
+A QuickScan scanner is only ever invoked once for a given source file,
+and it is only invoked if the file is used by some target in the tree
+(i.e., there is a dependency on the source file).
+
+QuickScan is invoked as follows:
+
+ QuickScan CONSENV CODEREF, FILENAME [, PATH]
+
+The subroutine referenced by CODEREF is expected to return a list of
+filenames included directly by FILE. These filenames will, in turn, be
+scanned. The optional PATH argument supplies a lookup path for finding
+FILENAME and/or files returned by the user-supplied subroutine. The PATH
+may be a reference to an array of lookup-directory names, or a string of
+names separated by the system's separator character (':' on UNIX systems,
+';' on Windows NT).
+
+The subroutine is called once for each line in the file, with $_ set to the
+current line. If the subroutine needs to look at additional lines, or, for
+that matter, the entire file, then it may read them itself, from the
+filehandle SCAN. It may also terminate the loop, if it knows that no further
+include information is available, by closing the filehandle.
+
+Whether or not a lookup path is provided, QuickScan first tries to lookup
+the file relative to the current directory (for the top-level file
+supplied directly to QuickScan), or from the directory containing the
+file which referenced the file. This is not very general, but seems good
+enough--especially if you have the luxury of writing your own utilities
+and can control the use of the search path in a standard way.
+
+Here's a real example, taken from a F<Construct> file here:
+
+ sub cons::SMFgen {
+ my($env, @tables) = @_;
+ foreach $t (@tables) {
+ $env->QuickScan(sub { /\b\S*?\.smf\b/g }, "$t.smf",
+ $env->{SMF_INCLUDE_PATH});
+ $env->Command(["$t.smdb.cc","$t.smdb.h","$t.snmp.cc",
+ "$t.ami.cc", "$t.http.cc"], "$t.smf",
+ q(smfgen %( %SMF_INCLUDE_OPT %) %<));
+ }
+ }
+
+The subroutine above finds all names of the form <name>.smf in the
+file. It will return the names even if they're found within comments,
+but that's OK (the mechanism is forgiving of extra files; they're just
+ignored on the assumption that the missing file will be noticed when
+the program, in this example, smfgen, is actually invoked).
+
+[NOTE that the form C<$env-E<gt>QuickScan ...> and C<$env-E<gt>Command
+...> should not be necessary, but, for some reason, is required
+for this particular invocation. This appears to be a bug in Perl or
+a misunderstanding on my part; this invocation style does not always
+appear to be necessary.]
+
+Here is another way to build the same scanner. This one uses an
+explicit code reference, and also (unnecessarily, in this case) reads
+the whole file itself:
+
+ sub myscan {
+ my(@includes);
+ do {
+ push(@includes, /\b\S*?\.smf\b/g);
+ } while <SCAN>;
+ @includes
+ }
+
+Note that the order of the loop is reversed, with the loop test at the
+end. This is because the first line is already read for you. This scanner
+can be attached to a source file by:
+
+ QuickScan $env \&myscan, "$_.smf";
+
+This final example, which scans a different type of input file, takes
+over the file scanning rather than being called for each input line:
+
+ $env->QuickScan(
+ sub { my(@includes) = ();
+ do {
+ push(@includes, $3)
+ if /^(#include|import)\s+(\")(.+)(\")/ && $3
+ } while <SCAN>;
+ @includes
+ },
+ "$idlFileName",
+ "$env->{CPPPATH};$BUILD/ActiveContext/ACSCLientInterfaces"
+ );
+
+=head1 SUPPORT AND SUGGESTIONS
+
+Cons is maintained by the user community. To subscribe, send mail to
+B<cons-discuss-request@gnu.org> with body B<subscribe>.
+
+Please report any suggestions through the B<cons-discuss@gnu.org> mailing
+list.
+
+=head1 BUGS
+
+Sure to be some. Please report any bugs through the B<bug-cons@gnu.org>
+mailing list.
+
+=head1 INFORMATION ABOUT CONS
+
+Information about CONS can be obtained from the official cons web site
+B<http://www.dsmit.com/cons/> or its mirrors listed there.
+
+The cons maintainers can be contacted by email at
+B<cons-maintainers@gnu.org>
+
+=head1 AUTHORS
+
+Originally by Bob Sidebotham. Then significantly enriched by the members
+of the Cons community B<cons-discuss@gnu.org>.
+
+The Cons community would like to thank Ulrich Pfeifer for the original pod
+documentation derived from the F<cons.html> file. Cons documentation is now
+a part of the program itself.
+
+=cut
diff --git a/code/unix/extract_ver.pl b/code/unix/extract_ver.pl
new file mode 100755
index 0000000..4193d68
--- /dev/null
+++ b/code/unix/extract_ver.pl
@@ -0,0 +1,9 @@
+#!/usr/bin/env perl
+# extracting version string from game/q_shared.h
+# hacked from Wolf build process
+
+# extract the wolf version from q_shared.h
+$line = `cat ../game/q_shared.h | grep Q3_VERSION`;
+chomp $line;
+$line =~ s/.*Q3\ (.*)\"/$1/;
+print "$line\n";
diff --git a/code/unix/ftol.nasm b/code/unix/ftol.nasm
new file mode 100755
index 0000000..772e6a0
--- /dev/null
+++ b/code/unix/ftol.nasm
@@ -0,0 +1,151 @@
+;===========================================================================
+;Copyright (C) 1999-2005 Id Software, Inc.
+;
+;This file is part of Quake III Arena source code.
+;
+;Quake III Arena source code is free software; you can redistribute it
+;and/or modify it under the terms of the GNU General Public License as
+;published by the Free Software Foundation; either version 2 of the License,
+;or (at your option) any later version.
+;
+;Quake III Arena source code is distributed in the hope that it will be
+;useful, but WITHOUT ANY WARRANTY; without even the implied warranty of
+;MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+;GNU General Public License for more details.
+;
+;You should have received a copy of the GNU General Public License
+;along with Foobar; if not, write to the Free Software
+;Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+;===========================================================================
+
+;
+; qftol -- fast floating point to long conversion.
+;
+
+segment .data
+
+temp dd 0.0
+fpucw dd 0
+
+; Precision Control Field , 2 bits / 0x0300
+; PC24 0x0000 Single precision (24 bits).
+; PC53 0x0200 Double precision (53 bits).
+; PC64 0x0300 Extended precision (64 bits).
+
+; Rounding Control Field, 2 bits / 0x0C00
+; RCN 0x0000 Rounding to nearest (even).
+; RCD 0x0400 Rounding down (directed, minus).
+; RCU 0x0800 Rounding up (directed plus).
+; RC0 0x0C00 Rounding towards zero (chop mode).
+
+
+; rounding towards nearest (even)
+cw027F dd 0x027F ; double precision
+cw037F dd 0x037F ; extended precision
+
+; rounding towards zero (chop mode)
+cw0E7F dd 0x0E7F ; double precision
+cw0F7F dd 0x0F7F ; extended precision
+
+
+segment .text
+
+;
+; int qftol( void ) - default control word
+;
+
+global qftol
+
+qftol:
+ fistp dword [temp]
+ mov eax, [temp]
+ ret
+
+
+;
+; int qftol027F( void ) - DirectX FPU
+;
+
+global qftol027F
+
+qftol027F:
+ fnstcw [fpucw]
+ fldcw [cw027F]
+ fistp dword [temp]
+ fldcw [fpucw]
+ mov eax, [temp]
+ ret
+
+;
+; int qftol037F( void ) - Linux FPU
+;
+
+global qftol037F
+
+qftol037F:
+ fnstcw [fpucw]
+ fldcw [cw037F]
+ fistp dword [temp]
+ fldcw [fpucw]
+ mov eax, [temp]
+ ret
+
+
+;
+; int qftol0F7F( void ) - ANSI
+;
+
+global qftol0F7F
+
+qftol0F7F:
+ fnstcw [fpucw]
+ fldcw [cw0F7F]
+ fistp dword [temp]
+ fldcw [fpucw]
+ mov eax, [temp]
+ ret
+
+;
+; int qftol0E7F( void )
+;
+
+global qftol0E7F
+
+qftol0E7F:
+ fnstcw [fpucw]
+ fldcw [cw0E7F]
+ fistp dword [temp]
+ fldcw [fpucw]
+ mov eax, [temp]
+ ret
+
+
+
+;
+; long Q_ftol( float q )
+;
+
+global Q_ftol
+
+Q_ftol:
+ fld dword [esp+4]
+ fistp dword [temp]
+ mov eax, [temp]
+ ret
+
+
+;
+; long qftol0F7F( float q ) - Linux FPU
+;
+
+global Q_ftol0F7F
+
+Q_ftol0F7F:
+ fnstcw [fpucw]
+ fld dword [esp+4]
+ fldcw [cw0F7F]
+ fistp dword [temp]
+ fldcw [fpucw]
+ mov eax, [temp]
+ ret
+
diff --git a/code/unix/linux_common.c b/code/unix/linux_common.c
new file mode 100755
index 0000000..512d425
--- /dev/null
+++ b/code/unix/linux_common.c
@@ -0,0 +1,344 @@
+/*
+===========================================================================
+Copyright (C) 1999-2005 Id Software, Inc.
+
+This file is part of Quake III Arena source code.
+
+Quake III Arena source code is free software; you can redistribute it
+and/or modify it under the terms of the GNU General Public License as
+published by the Free Software Foundation; either version 2 of the License,
+or (at your option) any later version.
+
+Quake III Arena source code is distributed in the hope that it will be
+useful, but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with Foobar; if not, write to the Free Software
+Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+===========================================================================
+*/
+/**
+ * GAS syntax equivalents of the MSVC asm memory calls in common.c
+ *
+ * The following changes have been made to the asm:
+ * 1. Registers are loaded by the inline asm arguments when possible
+ * 2. Labels have been changed to local label format (0,1,etc.) to allow inlining
+ *
+ * HISTORY:
+ * AH - Created on 08 Dec 2000
+ */
+
+#include <unistd.h> // AH - for size_t
+#include <string.h>
+
+// bk001207 - we need something under Linux, too. Mac?
+#if 1 // defined(C_ONLY) // bk010102 - dedicated?
+void Com_Memcpy (void* dest, const void* src, const size_t count) {
+ memcpy(dest, src, count);
+}
+
+void Com_Memset (void* dest, const int val, const size_t count) {
+ memset(dest, val, count);
+}
+
+#else
+
+typedef enum {
+ PRE_READ, // prefetch assuming that buffer is used for reading only
+ PRE_WRITE, // prefetch assuming that buffer is used for writing only
+ PRE_READ_WRITE // prefetch assuming that buffer is used for both reading and writing
+} e_prefetch;
+
+void Com_Prefetch (const void *s, const unsigned int bytes, e_prefetch type);
+
+void _copyDWord (unsigned int* dest, const unsigned int constant, const unsigned int count) {
+ // MMX version not used on standard Pentium MMX
+ // because the dword version is faster (with
+ // proper destination prefetching)
+ __asm__ __volatile__ (" \
+ //mov eax,constant // eax = val \
+ //mov edx,dest // dest \
+ //mov ecx,count \
+ movd %%eax, %%mm0 \
+ punpckldq %%mm0, %%mm0 \
+\
+ // ensure that destination is qword aligned \
+\
+ testl $7, %%edx // qword padding?\
+ jz 0f \
+ movl %%eax, (%%edx) \
+ decl %%ecx \
+ addl $4, %%edx \
+\
+0: movl %%ecx, %%ebx \
+ andl $0xfffffff0, %%ecx \
+ jz 2f \
+ jmp 1f \
+ .align 16 \
+\
+ // funny ordering here to avoid commands \
+ // that cross 32-byte boundaries (the \
+ // [edx+0] version has a special 3-byte opcode... \
+1: movq %%mm0, 8(%%edx) \
+ movq %%mm0, 16(%%edx) \
+ movq %%mm0, 24(%%edx) \
+ movq %%mm0, 32(%%edx) \
+ movq %%mm0, 40(%%edx) \
+ movq %%mm0, 48(%%edx) \
+ movq %%mm0, 56(%%edx) \
+ movq %%mm0, (%%edx)\
+ addl $64, %%edx \
+ subl $16, %%ecx \
+ jnz 1b \
+2: \
+ movl %%ebx, %%ecx // ebx = cnt \
+ andl $0xfffffff0, %%ecx // ecx = cnt&~15 \
+ subl %%ecx, %%ebx \
+ jz 6f \
+ cmpl $8, %%ebx \
+ jl 3f \
+\
+ movq %%mm0, (%%edx) \
+ movq %%mm0, 8(%%edx) \
+ movq %%mm0, 16(%%edx) \
+ movq %%mm0, 24(%%edx) \
+ addl $32, %%edx \
+ subl $8, %%ebx \
+ jz 6f \
+\
+3: cmpl $4, %%ebx \
+ jl 4f \
+ \
+ movq %%mm0, (%%edx) \
+ movq %%mm0, 8(%%edx) \
+ addl $16, %%edx \
+ subl $4, %%ebx \
+\
+4: cmpl $2, %%ebx \
+ jl 5f \
+ movq %%mm0, (%%edx) \
+ addl $8, %%edx \
+ subl $2, %%ebx \
+\
+5: cmpl $1, %%ebx \
+ jl 6f \
+ movl %%eax, (%%edx) \
+6: \
+ emms \
+ "
+ : : "a" (constant), "c" (count), "d" (dest)
+ : "%ebx", "%edi", "%esi", "cc", "memory");
+}
+
+// optimized memory copy routine that handles all alignment
+// cases and block sizes efficiently
+void Com_Memcpy (void* dest, const void* src, const size_t count) {
+ Com_Prefetch (src, count, PRE_READ);
+ __asm__ __volatile__ (" \
+ pushl %%edi \
+ pushl %%esi \
+ //mov ecx,count \
+ cmpl $0, %%ecx // count = 0 check (just to be on the safe side) \
+ je 6f \
+ //mov edx,dest \
+ movl %0, %%ebx \
+ cmpl $32, %%ecx // padding only? \
+ jl 1f \
+\
+ movl %%ecx, %%edi \
+ andl $0xfffffe00, %%edi // edi = count&~31 \
+ subl $32, %%edi \
+\
+ .align 16 \
+0: \
+ movl (%%ebx, %%edi, 1), %%eax \
+ movl 4(%%ebx, %%edi, 1), %%esi \
+ movl %%eax, (%%edx, %%edi, 1) \
+ movl %%esi, 4(%%edx, %%edi, 1) \
+ movl 8(%%ebx, %%edi, 1), %%eax \
+ movl 12(%%ebx, %%edi, 1), %%esi \
+ movl %%eax, 8(%%edx, %%edi, 1) \
+ movl %%esi, 12(%%edx, %%edi, 1) \
+ movl 16(%%ebx, %%edi, 1), %%eax \
+ movl 20(%%ebx, %%edi, 1), %%esi \
+ movl %%eax, 16(%%edx, %%edi, 1) \
+ movl %%esi, 20(%%edx, %%edi, 1) \
+ movl 24(%%ebx, %%edi, 1), %%eax \
+ movl 28(%%ebx, %%edi, 1), %%esi \
+ movl %%eax, 24(%%edx, %%edi, 1) \
+ movl %%esi, 28(%%edx, %%edi, 1) \
+ subl $32, %%edi \
+ jge 0b \
+ \
+ movl %%ecx, %%edi \
+ andl $0xfffffe00, %%edi \
+ addl %%edi, %%ebx // increase src pointer \
+ addl %%edi, %%edx // increase dst pointer \
+ andl $31, %%ecx // new count \
+ jz 6f // if count = 0, get outta here \
+\
+1: \
+ cmpl $16, %%ecx \
+ jl 2f \
+ movl (%%ebx), %%eax \
+ movl %%eax, (%%edx) \
+ movl 4(%%ebx), %%eax \
+ movl %%eax, 4(%%edx) \
+ movl 8(%%ebx), %%eax \
+ movl %%eax, 8(%%edx) \
+ movl 12(%%ebx), %%eax \
+ movl %%eax, 12(%%edx) \
+ subl $16, %%ecx \
+ addl $16, %%ebx \
+ addl $16, %%edx \
+2: \
+ cmpl $8, %%ecx \
+ jl 3f \
+ movl (%%ebx), %%eax \
+ movl %%eax, (%%edx) \
+ movl 4(%%ebx), %%eax \
+ subl $8, %%ecx \
+ movl %%eax, 4(%%edx) \
+ addl $8, %%ebx \
+ addl $8, %%edx \
+3: \
+ cmpl $4, %%ecx \
+ jl 4f \
+ movl (%%ebx), %%eax // here 4-7 bytes \
+ addl $4, %%ebx \
+ subl $4, %%ecx \
+ movl %%eax, (%%edx) \
+ addl $4, %%edx \
+4: // 0-3 remaining bytes \
+ cmpl $2, %%ecx \
+ jl 5f \
+ movw (%%ebx), %%ax // two bytes \
+ cmpl $3, %%ecx // less than 3? \
+ movw %%ax, (%%edx) \
+ jl 6f \
+ movb 2(%%ebx), %%al // last byte \
+ movb %%al, 2(%%edx) \
+ jmp 6f \
+5: \
+ cmpl $1, %%ecx \
+ jl 6f \
+ movb (%%ebx), %%al \
+ movb %%al, (%%edx) \
+6: \
+ popl %%esi \
+ popl %%edi \
+ "
+ : : "m" (src), "d" (dest), "c" (count)
+ : "%eax", "%ebx", "%edi", "%esi", "cc", "memory");
+}
+
+void Com_Memset (void* dest, const int val, const size_t count)
+{
+ unsigned int fillval;
+
+ if (count < 8)
+ {
+ __asm__ __volatile__ (" \
+ //mov edx,dest \
+ //mov eax, val \
+ movb %%al, %%ah \
+ movl %%eax, %%ebx \
+ andl $0xffff, %%ebx \
+ shll $16, %%eax \
+ addl %%ebx, %%eax // eax now contains pattern \
+ //mov ecx,count \
+ cmpl $4, %%ecx \
+ jl 0f \
+ movl %%eax, (%%edx) // copy first dword \
+ addl $4, %%edx \
+ subl $4, %%ecx \
+ 0: cmpl $2, %%ecx \
+ jl 1f \
+ movw %%ax, (%%edx) // copy 2 bytes \
+ addl $2, %%edx \
+ subl $2, %%ecx \
+ 1: cmpl $0, %%ecx \
+ je 2f \
+ movb %%al, (%%edx) // copy single byte \
+ 2: \
+ "
+ : : "d" (dest), "a" (val), "c" (count)
+ : "%ebx", "%edi", "%esi", "cc", "memory");
+
+ return;
+ }
+
+ fillval = val;
+
+ fillval = fillval|(fillval<<8);
+ fillval = fillval|(fillval<<16); // fill dword with 8-bit pattern
+
+ _copyDWord ((unsigned int*)(dest),fillval, count/4);
+
+ __asm__ __volatile__ (" // padding of 0-3 bytes \
+ //mov ecx,count \
+ movl %%ecx, %%eax \
+ andl $3, %%ecx \
+ jz 1f \
+ andl $0xffffff00, %%eax \
+ //mov ebx,dest \
+ addl %%eax, %%edx \
+ movl %0, %%eax \
+ cmpl $2, %%ecx \
+ jl 0f \
+ movw %%ax, (%%edx) \
+ cmpl $2, %%ecx \
+ je 1f \
+ movb %%al, 2(%%edx) \
+ jmp 1f \
+0: \
+ cmpl $0, %%ecx\
+ je 1f\
+ movb %%al, (%%edx)\
+1: \
+ "
+ : : "m" (fillval), "c" (count), "d" (dest)
+ : "%eax", "%ebx", "%edi", "%esi", "cc", "memory");
+}
+
+void Com_Prefetch (const void *s, const unsigned int bytes, e_prefetch type)
+{
+ // write buffer prefetching is performed only if
+ // the processor benefits from it. Read and read/write
+ // prefetching is always performed.
+
+ switch (type)
+ {
+ case PRE_WRITE : break;
+ case PRE_READ:
+ case PRE_READ_WRITE:
+
+ __asm__ __volatile__ ("\
+ //mov ebx,s\
+ //mov ecx,bytes\
+ cmpl $4096, %%ecx // clamp to 4kB\
+ jle 0f\
+ movl $4096, %%ecx\
+ 0:\
+ addl $0x1f, %%ecx\
+ shrl $5, %%ecx // number of cache lines\
+ jz 2f\
+ jmp 1f\
+\
+ .align 16\
+ 1: testb %%al, (%%edx)\
+ addl $32, %%edx\
+ decl %%ecx\
+ jnz 1b\
+ 2:\
+ "
+ : : "d" (s), "c" (bytes)
+ : "%eax", "%ebx", "%edi", "%esi", "memory", "cc");
+
+ break;
+ }
+}
+
+#endif
diff --git a/code/unix/linux_glimp.c b/code/unix/linux_glimp.c
new file mode 100755
index 0000000..fa92501
--- /dev/null
+++ b/code/unix/linux_glimp.c
@@ -0,0 +1,1780 @@
+/*
+===========================================================================
+Copyright (C) 1999-2005 Id Software, Inc.
+
+This file is part of Quake III Arena source code.
+
+Quake III Arena source code is free software; you can redistribute it
+and/or modify it under the terms of the GNU General Public License as
+published by the Free Software Foundation; either version 2 of the License,
+or (at your option) any later version.
+
+Quake III Arena source code is distributed in the hope that it will be
+useful, but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with Foobar; if not, write to the Free Software
+Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+===========================================================================
+*/
+/*
+** GLW_IMP.C
+**
+** This file contains ALL Linux specific stuff having to do with the
+** OpenGL refresh. When a port is being made the following functions
+** must be implemented by the port:
+**
+** GLimp_EndFrame
+** GLimp_Init
+** GLimp_Shutdown
+** GLimp_SwitchFullscreen
+** GLimp_SetGamma
+**
+*/
+
+#include <termios.h>
+#include <sys/ioctl.h>
+#ifdef __linux__
+ #include <sys/stat.h>
+ #include <sys/vt.h>
+#endif
+#include <stdarg.h>
+#include <stdio.h>
+#include <signal.h>
+#include <pthread.h>
+#include <semaphore.h>
+
+// bk001204
+#include <dlfcn.h>
+
+// bk001206 - from my Heretic2 by way of Ryan's Fakk2
+// Needed for the new X11_PendingInput() function.
+#include <sys/time.h>
+#include <sys/types.h>
+#include <unistd.h>
+
+#include "../renderer/tr_local.h"
+#include "../client/client.h"
+#include "linux_local.h" // bk001130
+
+#include "unix_glw.h"
+
+#include <GL/glx.h>
+
+#include <X11/keysym.h>
+#include <X11/cursorfont.h>
+
+#include <X11/extensions/xf86dga.h>
+#include <X11/extensions/xf86vmode.h>
+
+#define WINDOW_CLASS_NAME "Quake III: Arena"
+
+typedef enum
+{
+ RSERR_OK,
+
+ RSERR_INVALID_FULLSCREEN,
+ RSERR_INVALID_MODE,
+
+ RSERR_UNKNOWN
+} rserr_t;
+
+glwstate_t glw_state;
+
+static Display *dpy = NULL;
+static int scrnum;
+static Window win = 0;
+static GLXContext ctx = NULL;
+
+// bk001206 - not needed anymore
+// static qboolean autorepeaton = qtrue;
+
+#define KEY_MASK (KeyPressMask | KeyReleaseMask)
+#define MOUSE_MASK (ButtonPressMask | ButtonReleaseMask | \
+ PointerMotionMask | ButtonMotionMask )
+#define X_MASK (KEY_MASK | MOUSE_MASK | VisibilityChangeMask | StructureNotifyMask )
+
+static qboolean mouse_avail;
+static qboolean mouse_active = qfalse;
+static int mwx, mwy;
+static int mx = 0, my = 0;
+
+// Time mouse was reset, we ignore the first 50ms of the mouse to allow settling of events
+static int mouseResetTime = 0;
+#define MOUSE_RESET_DELAY 50
+
+static cvar_t *in_mouse;
+static cvar_t *in_dgamouse; // user pref for dga mouse
+cvar_t *in_subframe;
+cvar_t *in_nograb; // this is strictly for developers
+
+// bk001130 - from cvs1.17 (mkv), but not static
+cvar_t *in_joystick = NULL;
+cvar_t *in_joystickDebug = NULL;
+cvar_t *joy_threshold = NULL;
+
+cvar_t *r_allowSoftwareGL; // don't abort out if the pixelformat claims software
+cvar_t *r_previousglDriver;
+
+qboolean vidmode_ext = qfalse;
+static int vidmode_MajorVersion = 0, vidmode_MinorVersion = 0; // major and minor of XF86VidExtensions
+
+// gamma value of the X display before we start playing with it
+static XF86VidModeGamma vidmode_InitialGamma;
+
+static int win_x, win_y;
+
+static XF86VidModeModeInfo **vidmodes;
+//static int default_dotclock_vidmode; // bk001204 - unused
+static int num_vidmodes;
+static qboolean vidmode_active = qfalse;
+
+static int mouse_accel_numerator;
+static int mouse_accel_denominator;
+static int mouse_threshold;
+
+/*
+* Find the first occurrence of find in s.
+*/
+// bk001130 - from cvs1.17 (mkv), const
+// bk001130 - made first argument const
+static const char *Q_stristr( const char *s, const char *find)
+{
+ register char c, sc;
+ register size_t len;
+
+ if ((c = *find++) != 0)
+ {
+ if (c >= 'a' && c <= 'z')
+ {
+ c -= ('a' - 'A');
+ }
+ len = strlen(find);
+ do
+ {
+ do
+ {
+ if ((sc = *s++) == 0)
+ return NULL;
+ if (sc >= 'a' && sc <= 'z')
+ {
+ sc -= ('a' - 'A');
+ }
+ } while (sc != c);
+ } while (Q_stricmpn(s, find, len) != 0);
+ s--;
+ }
+ return s;
+}
+
+/*****************************************************************************
+** KEYBOARD
+** NOTE TTimo the keyboard handling is done with KeySyms
+** that means relying on the keyboard mapping provided by X
+** in-game it would probably be better to use KeyCode (i.e. hardware key codes)
+** you would still need the KeySyms in some cases, such as for the console and all entry textboxes
+** (cause there's nothing worse than a qwerty mapping on a french keyboard)
+**
+** you can turn on some debugging and verbose of the keyboard code with #define KBD_DBG
+******************************************************************************/
+
+//#define KBD_DBG
+
+static char *XLateKey(XKeyEvent *ev, int *key)
+{
+ static char buf[64];
+ KeySym keysym;
+ int XLookupRet;
+
+ *key = 0;
+
+ XLookupRet = XLookupString(ev, buf, sizeof buf, &keysym, 0);
+#ifdef KBD_DBG
+ ri.Printf(PRINT_ALL, "XLookupString ret: %d buf: %s keysym: %x\n", XLookupRet, buf, keysym);
+#endif
+
+ switch (keysym)
+ {
+ case XK_KP_Page_Up:
+ case XK_KP_9: *key = K_KP_PGUP; break;
+ case XK_Page_Up: *key = K_PGUP; break;
+
+ case XK_KP_Page_Down:
+ case XK_KP_3: *key = K_KP_PGDN; break;
+ case XK_Page_Down: *key = K_PGDN; break;
+
+ case XK_KP_Home: *key = K_KP_HOME; break;
+ case XK_KP_7: *key = K_KP_HOME; break;
+ case XK_Home: *key = K_HOME; break;
+
+ case XK_KP_End:
+ case XK_KP_1: *key = K_KP_END; break;
+ case XK_End: *key = K_END; break;
+
+ case XK_KP_Left: *key = K_KP_LEFTARROW; break;
+ case XK_KP_4: *key = K_KP_LEFTARROW; break;
+ case XK_Left: *key = K_LEFTARROW; break;
+
+ case XK_KP_Right: *key = K_KP_RIGHTARROW; break;
+ case XK_KP_6: *key = K_KP_RIGHTARROW; break;
+ case XK_Right: *key = K_RIGHTARROW; break;
+
+ case XK_KP_Down:
+ case XK_KP_2: *key = K_KP_DOWNARROW; break;
+ case XK_Down: *key = K_DOWNARROW; break;
+
+ case XK_KP_Up:
+ case XK_KP_8: *key = K_KP_UPARROW; break;
+ case XK_Up: *key = K_UPARROW; break;
+
+ case XK_Escape: *key = K_ESCAPE; break;
+
+ case XK_KP_Enter: *key = K_KP_ENTER; break;
+ case XK_Return: *key = K_ENTER; break;
+
+ case XK_Tab: *key = K_TAB; break;
+
+ case XK_F1: *key = K_F1; break;
+
+ case XK_F2: *key = K_F2; break;
+
+ case XK_F3: *key = K_F3; break;
+
+ case XK_F4: *key = K_F4; break;
+
+ case XK_F5: *key = K_F5; break;
+
+ case XK_F6: *key = K_F6; break;
+
+ case XK_F7: *key = K_F7; break;
+
+ case XK_F8: *key = K_F8; break;
+
+ case XK_F9: *key = K_F9; break;
+
+ case XK_F10: *key = K_F10; break;
+
+ case XK_F11: *key = K_F11; break;
+
+ case XK_F12: *key = K_F12; break;
+
+ // bk001206 - from Ryan's Fakk2
+ //case XK_BackSpace: *key = 8; break; // ctrl-h
+ case XK_BackSpace: *key = K_BACKSPACE; break; // ctrl-h
+
+ case XK_KP_Delete:
+ case XK_KP_Decimal: *key = K_KP_DEL; break;
+ case XK_Delete: *key = K_DEL; break;
+
+ case XK_Pause: *key = K_PAUSE; break;
+
+ case XK_Shift_L:
+ case XK_Shift_R: *key = K_SHIFT; break;
+
+ case XK_Execute:
+ case XK_Control_L:
+ case XK_Control_R: *key = K_CTRL; break;
+
+ case XK_Alt_L:
+ case XK_Meta_L:
+ case XK_Alt_R:
+ case XK_Meta_R: *key = K_ALT; break;
+
+ case XK_KP_Begin: *key = K_KP_5; break;
+
+ case XK_Insert: *key = K_INS; break;
+ case XK_KP_Insert:
+ case XK_KP_0: *key = K_KP_INS; break;
+
+ case XK_KP_Multiply: *key = '*'; break;
+ case XK_KP_Add: *key = K_KP_PLUS; break;
+ case XK_KP_Subtract: *key = K_KP_MINUS; break;
+ case XK_KP_Divide: *key = K_KP_SLASH; break;
+
+ // bk001130 - from cvs1.17 (mkv)
+ case XK_exclam: *key = '1'; break;
+ case XK_at: *key = '2'; break;
+ case XK_numbersign: *key = '3'; break;
+ case XK_dollar: *key = '4'; break;
+ case XK_percent: *key = '5'; break;
+ case XK_asciicircum: *key = '6'; break;
+ case XK_ampersand: *key = '7'; break;
+ case XK_asterisk: *key = '8'; break;
+ case XK_parenleft: *key = '9'; break;
+ case XK_parenright: *key = '0'; break;
+
+ // weird french keyboards ..
+ // NOTE: console toggle is hardcoded in cl_keys.c, can't be unbound
+ // cleaner would be .. using hardware key codes instead of the key syms
+ // could also add a new K_KP_CONSOLE
+ case XK_twosuperior: *key = '~'; break;
+
+ // https://zerowing.idsoftware.com/bugzilla/show_bug.cgi?id=472
+ case XK_space:
+ case XK_KP_Space: *key = K_SPACE; break;
+
+ default:
+ if (XLookupRet == 0)
+ {
+ if (com_developer->value)
+ {
+ ri.Printf(PRINT_ALL, "Warning: XLookupString failed on KeySym %d\n", keysym);
+ }
+ return NULL;
+ }
+ else
+ {
+ // XK_* tests failed, but XLookupString got a buffer, so let's try it
+ *key = *(unsigned char *)buf;
+ if (*key >= 'A' && *key <= 'Z')
+ *key = *key - 'A' + 'a';
+ // if ctrl is pressed, the keys are not between 'A' and 'Z', for instance ctrl-z == 26 ^Z ^C etc.
+ // see https://zerowing.idsoftware.com/bugzilla/show_bug.cgi?id=19
+ else if (*key >= 1 && *key <= 26)
+ *key = *key + 'a' - 1;
+ }
+ break;
+ }
+
+ return buf;
+}
+
+// ========================================================================
+// makes a null cursor
+// ========================================================================
+
+static Cursor CreateNullCursor(Display *display, Window root)
+{
+ Pixmap cursormask;
+ XGCValues xgc;
+ GC gc;
+ XColor dummycolour;
+ Cursor cursor;
+
+ cursormask = XCreatePixmap(display, root, 1, 1, 1/*depth*/);
+ xgc.function = GXclear;
+ gc = XCreateGC(display, cursormask, GCFunction, &xgc);
+ XFillRectangle(display, cursormask, gc, 0, 0, 1, 1);
+ dummycolour.pixel = 0;
+ dummycolour.red = 0;
+ dummycolour.flags = 04;
+ cursor = XCreatePixmapCursor(display, cursormask, cursormask,
+ &dummycolour,&dummycolour, 0,0);
+ XFreePixmap(display,cursormask);
+ XFreeGC(display,gc);
+ return cursor;
+}
+
+static void install_grabs(void)
+{
+ // inviso cursor
+ XWarpPointer(dpy, None, win,
+ 0, 0, 0, 0,
+ glConfig.vidWidth / 2, glConfig.vidHeight / 2);
+ XSync(dpy, False);
+
+ XDefineCursor(dpy, win, CreateNullCursor(dpy, win));
+
+ XGrabPointer(dpy, win, // bk010108 - do this earlier?
+ False,
+ MOUSE_MASK,
+ GrabModeAsync, GrabModeAsync,
+ win,
+ None,
+ CurrentTime);
+
+ XGetPointerControl(dpy, &mouse_accel_numerator, &mouse_accel_denominator,
+ &mouse_threshold);
+
+ XChangePointerControl(dpy, True, True, 1, 1, 0);
+
+ XSync(dpy, False);
+
+ mouseResetTime = Sys_Milliseconds ();
+
+ if (in_dgamouse->value)
+ {
+ int MajorVersion, MinorVersion;
+
+ if (!XF86DGAQueryVersion(dpy, &MajorVersion, &MinorVersion))
+ {
+ // unable to query, probalby not supported, force the setting to 0
+ ri.Printf( PRINT_ALL, "Failed to detect XF86DGA Mouse\n" );
+ ri.Cvar_Set( "in_dgamouse", "0" );
+ } else
+ {
+ XF86DGADirectVideo(dpy, DefaultScreen(dpy), XF86DGADirectMouse);
+ XWarpPointer(dpy, None, win, 0, 0, 0, 0, 0, 0);
+ }
+ } else
+ {
+ mwx = glConfig.vidWidth / 2;
+ mwy = glConfig.vidHeight / 2;
+ mx = my = 0;
+ }
+
+ XGrabKeyboard(dpy, win,
+ False,
+ GrabModeAsync, GrabModeAsync,
+ CurrentTime);
+
+ XSync(dpy, False);
+}
+
+static void uninstall_grabs(void)
+{
+ if (in_dgamouse->value)
+ {
+ if (com_developer->value)
+ ri.Printf( PRINT_ALL, "DGA Mouse - Disabling DGA DirectVideo\n" );
+ XF86DGADirectVideo(dpy, DefaultScreen(dpy), 0);
+ }
+
+ XChangePointerControl(dpy, qtrue, qtrue, mouse_accel_numerator,
+ mouse_accel_denominator, mouse_threshold);
+
+ XUngrabPointer(dpy, CurrentTime);
+ XUngrabKeyboard(dpy, CurrentTime);
+
+ XWarpPointer(dpy, None, win,
+ 0, 0, 0, 0,
+ glConfig.vidWidth / 2, glConfig.vidHeight / 2);
+
+ // inviso cursor
+ XUndefineCursor(dpy, win);
+}
+
+// bk001206 - from Ryan's Fakk2
+/**
+ * XPending() actually performs a blocking read
+ * if no events available. From Fakk2, by way of
+ * Heretic2, by way of SDL, original idea GGI project.
+ * The benefit of this approach over the quite
+ * badly behaved XAutoRepeatOn/Off is that you get
+ * focus handling for free, which is a major win
+ * with debug and windowed mode. It rests on the
+ * assumption that the X server will use the
+ * same timestamp on press/release event pairs
+ * for key repeats.
+ */
+static qboolean X11_PendingInput(void) {
+
+ assert(dpy != NULL);
+
+ // Flush the display connection
+ // and look to see if events are queued
+ XFlush( dpy );
+ if ( XEventsQueued( dpy, QueuedAlready) )
+ {
+ return qtrue;
+ }
+
+ // More drastic measures are required -- see if X is ready to talk
+ {
+ static struct timeval zero_time;
+ int x11_fd;
+ fd_set fdset;
+
+ x11_fd = ConnectionNumber( dpy );
+ FD_ZERO(&fdset);
+ FD_SET(x11_fd, &fdset);
+ if ( select(x11_fd+1, &fdset, NULL, NULL, &zero_time) == 1 )
+ {
+ return(XPending(dpy));
+ }
+ }
+
+ // Oh well, nothing is ready ..
+ return qfalse;
+}
+
+// bk001206 - from Ryan's Fakk2. See above.
+static qboolean repeated_press(XEvent *event)
+{
+ XEvent peekevent;
+ qboolean repeated = qfalse;
+
+ assert(dpy != NULL);
+
+ if (X11_PendingInput())
+ {
+ XPeekEvent(dpy, &peekevent);
+
+ if ((peekevent.type == KeyPress) &&
+ (peekevent.xkey.keycode == event->xkey.keycode) &&
+ (peekevent.xkey.time == event->xkey.time))
+ {
+ repeated = qtrue;
+ XNextEvent(dpy, &peekevent); // skip event.
+ } // if
+ } // if
+
+ return(repeated);
+} // repeated_press
+
+int Sys_XTimeToSysTime (Time xtime);
+static void HandleEvents(void)
+{
+ int b;
+ int key;
+ XEvent event;
+ qboolean dowarp = qfalse;
+ char *p;
+ int dx, dy;
+ int t = 0; // default to 0 in case we don't set
+
+ if (!dpy)
+ return;
+
+ while (XPending(dpy))
+ {
+ XNextEvent(dpy, &event);
+ switch (event.type)
+ {
+ case KeyPress:
+ t = Sys_XTimeToSysTime(event.xkey.time);
+ p = XLateKey(&event.xkey, &key);
+ if (key)
+ {
+ Sys_QueEvent( t, SE_KEY, key, qtrue, 0, NULL );
+ }
+ if (p)
+ {
+ while (*p)
+ {
+ Sys_QueEvent( t, SE_CHAR, *p++, 0, 0, NULL );
+ }
+ }
+ break;
+
+ case KeyRelease:
+ t = Sys_XTimeToSysTime(event.xkey.time);
+ // bk001206 - handle key repeat w/o XAutRepatOn/Off
+ // also: not done if console/menu is active.
+ // From Ryan's Fakk2.
+ // see game/q_shared.h, KEYCATCH_* . 0 == in 3d game.
+ if (cls.keyCatchers == 0)
+ { // FIXME: KEYCATCH_NONE
+ if (repeated_press(&event) == qtrue)
+ continue;
+ } // if
+ XLateKey(&event.xkey, &key);
+
+ Sys_QueEvent( t, SE_KEY, key, qfalse, 0, NULL );
+ break;
+
+ case MotionNotify:
+ t = Sys_XTimeToSysTime(event.xkey.time);
+ if (mouse_active)
+ {
+ if (in_dgamouse->value)
+ {
+ if (abs(event.xmotion.x_root) > 1)
+ mx += event.xmotion.x_root * 2;
+ else
+ mx += event.xmotion.x_root;
+ if (abs(event.xmotion.y_root) > 1)
+ my += event.xmotion.y_root * 2;
+ else
+ my += event.xmotion.y_root;
+ if (t - mouseResetTime > MOUSE_RESET_DELAY )
+ {
+ Sys_QueEvent( t, SE_MOUSE, mx, my, 0, NULL );
+ }
+ mx = my = 0;
+ } else
+ {
+ // If it's a center motion, we've just returned from our warp
+ if (event.xmotion.x == glConfig.vidWidth/2 &&
+ event.xmotion.y == glConfig.vidHeight/2)
+ {
+ mwx = glConfig.vidWidth/2;
+ mwy = glConfig.vidHeight/2;
+ if (t - mouseResetTime > MOUSE_RESET_DELAY )
+ {
+ Sys_QueEvent( t, SE_MOUSE, mx, my, 0, NULL );
+ }
+ mx = my = 0;
+ break;
+ }
+
+ dx = ((int)event.xmotion.x - mwx);
+ dy = ((int)event.xmotion.y - mwy);
+ if (abs(dx) > 1)
+ mx += dx * 2;
+ else
+ mx += dx;
+ if (abs(dy) > 1)
+ my += dy * 2;
+ else
+ my += dy;
+
+ mwx = event.xmotion.x;
+ mwy = event.xmotion.y;
+ dowarp = qtrue;
+ }
+ }
+ break;
+
+ case ButtonPress:
+ t = Sys_XTimeToSysTime(event.xkey.time);
+ if (event.xbutton.button == 4)
+ {
+ Sys_QueEvent( t, SE_KEY, K_MWHEELUP, qtrue, 0, NULL );
+ } else if (event.xbutton.button == 5)
+ {
+ Sys_QueEvent( t, SE_KEY, K_MWHEELDOWN, qtrue, 0, NULL );
+ } else
+ {
+ // NOTE TTimo there seems to be a weird mapping for K_MOUSE1 K_MOUSE2 K_MOUSE3 ..
+ b=-1;
+ if (event.xbutton.button == 1)
+ {
+ b = 0; // K_MOUSE1
+ } else if (event.xbutton.button == 2)
+ {
+ b = 2; // K_MOUSE3
+ } else if (event.xbutton.button == 3)
+ {
+ b = 1; // K_MOUSE2
+ } else if (event.xbutton.button == 6)
+ {
+ b = 3; // K_MOUSE4
+ } else if (event.xbutton.button == 7)
+ {
+ b = 4; // K_MOUSE5
+ };
+
+ Sys_QueEvent( t, SE_KEY, K_MOUSE1 + b, qtrue, 0, NULL );
+ }
+ break;
+
+ case ButtonRelease:
+ t = Sys_XTimeToSysTime(event.xkey.time);
+ if (event.xbutton.button == 4)
+ {
+ Sys_QueEvent( t, SE_KEY, K_MWHEELUP, qfalse, 0, NULL );
+ } else if (event.xbutton.button == 5)
+ {
+ Sys_QueEvent( t, SE_KEY, K_MWHEELDOWN, qfalse, 0, NULL );
+ } else
+ {
+ b=-1;
+ if (event.xbutton.button == 1)
+ {
+ b = 0;
+ } else if (event.xbutton.button == 2)
+ {
+ b = 2;
+ } else if (event.xbutton.button == 3)
+ {
+ b = 1;
+ } else if (event.xbutton.button == 6)
+ {
+ b = 3; // K_MOUSE4
+ } else if (event.xbutton.button == 7)
+ {
+ b = 4; // K_MOUSE5
+ };
+ Sys_QueEvent( t, SE_KEY, K_MOUSE1 + b, qfalse, 0, NULL );
+ }
+ break;
+
+ case CreateNotify :
+ win_x = event.xcreatewindow.x;
+ win_y = event.xcreatewindow.y;
+ break;
+
+ case ConfigureNotify :
+ win_x = event.xconfigure.x;
+ win_y = event.xconfigure.y;
+ break;
+ }
+ }
+
+ if (dowarp)
+ {
+ XWarpPointer(dpy,None,win,0,0,0,0,
+ (glConfig.vidWidth/2),(glConfig.vidHeight/2));
+ }
+}
+
+// NOTE TTimo for the tty console input, we didn't rely on those ..
+// it's not very surprising actually cause they are not used otherwise
+void KBD_Init(void)
+{
+}
+
+void KBD_Close(void)
+{
+}
+
+void IN_ActivateMouse( void )
+{
+ if (!mouse_avail || !dpy || !win)
+ return;
+
+ if (!mouse_active)
+ {
+ if (!in_nograb->value)
+ install_grabs();
+ else if (in_dgamouse->value) // force dga mouse to 0 if using nograb
+ ri.Cvar_Set("in_dgamouse", "0");
+ mouse_active = qtrue;
+ }
+}
+
+void IN_DeactivateMouse( void )
+{
+ if (!mouse_avail || !dpy || !win)
+ return;
+
+ if (mouse_active)
+ {
+ if (!in_nograb->value)
+ uninstall_grabs();
+ else if (in_dgamouse->value) // force dga mouse to 0 if using nograb
+ ri.Cvar_Set("in_dgamouse", "0");
+ mouse_active = qfalse;
+ }
+}
+/*****************************************************************************/
+
+/*
+** GLimp_SetGamma
+**
+** This routine should only be called if glConfig.deviceSupportsGamma is TRUE
+*/
+void GLimp_SetGamma( unsigned char red[256], unsigned char green[256], unsigned char blue[256] )
+{
+ // NOTE TTimo we get the gamma value from cvar, because we can't work with the s_gammatable
+ // the API wasn't changed to avoid breaking other OSes
+ float g = Cvar_Get("r_gamma", "1.0", 0)->value;
+ XF86VidModeGamma gamma;
+ assert(glConfig.deviceSupportsGamma);
+ gamma.red = g;
+ gamma.green = g;
+ gamma.blue = g;
+ XF86VidModeSetGamma(dpy, scrnum, &gamma);
+}
+
+/*
+** GLimp_Shutdown
+**
+** This routine does all OS specific shutdown procedures for the OpenGL
+** subsystem. Under OpenGL this means NULLing out the current DC and
+** HGLRC, deleting the rendering context, and releasing the DC acquired
+** for the window. The state structure is also nulled out.
+**
+*/
+void GLimp_Shutdown( void )
+{
+ if (!ctx || !dpy)
+ return;
+ IN_DeactivateMouse();
+ // bk001206 - replaced with H2/Fakk2 solution
+ // XAutoRepeatOn(dpy);
+ // autorepeaton = qfalse; // bk001130 - from cvs1.17 (mkv)
+ if (dpy)
+ {
+ if (ctx)
+ qglXDestroyContext(dpy, ctx);
+ if (win)
+ XDestroyWindow(dpy, win);
+ if (vidmode_active)
+ XF86VidModeSwitchToMode(dpy, scrnum, vidmodes[0]);
+ if (glConfig.deviceSupportsGamma)
+ {
+ XF86VidModeSetGamma(dpy, scrnum, &vidmode_InitialGamma);
+ }
+ // NOTE TTimo opening/closing the display should be necessary only once per run
+ // but it seems QGL_Shutdown gets called in a lot of occasion
+ // in some cases, this XCloseDisplay is known to raise some X errors
+ // ( https://zerowing.idsoftware.com/bugzilla/show_bug.cgi?id=33 )
+ XCloseDisplay(dpy);
+ }
+ vidmode_active = qfalse;
+ dpy = NULL;
+ win = 0;
+ ctx = NULL;
+
+ memset( &glConfig, 0, sizeof( glConfig ) );
+ memset( &glState, 0, sizeof( glState ) );
+
+ QGL_Shutdown();
+}
+
+/*
+** GLimp_LogComment
+*/
+void GLimp_LogComment( char *comment )
+{
+ if ( glw_state.log_fp )
+ {
+ fprintf( glw_state.log_fp, "%s", comment );
+ }
+}
+
+/*
+** GLW_StartDriverAndSetMode
+*/
+// bk001204 - prototype needed
+int GLW_SetMode( const char *drivername, int mode, qboolean fullscreen );
+static qboolean GLW_StartDriverAndSetMode( const char *drivername,
+ int mode,
+ qboolean fullscreen )
+{
+ rserr_t err;
+
+ // don't ever bother going into fullscreen with a voodoo card
+#if 1 // JDC: I reenabled this
+ if ( Q_stristr( drivername, "Voodoo" ) )
+ {
+ ri.Cvar_Set( "r_fullscreen", "0" );
+ r_fullscreen->modified = qfalse;
+ fullscreen = qfalse;
+ }
+#endif
+
+ if (fullscreen && in_nograb->value)
+ {
+ ri.Printf( PRINT_ALL, "Fullscreen not allowed with in_nograb 1\n");
+ ri.Cvar_Set( "r_fullscreen", "0" );
+ r_fullscreen->modified = qfalse;
+ fullscreen = qfalse;
+ }
+
+ err = GLW_SetMode( drivername, mode, fullscreen );
+
+ switch ( err )
+ {
+ case RSERR_INVALID_FULLSCREEN:
+ ri.Printf( PRINT_ALL, "...WARNING: fullscreen unavailable in this mode\n" );
+ return qfalse;
+ case RSERR_INVALID_MODE:
+ ri.Printf( PRINT_ALL, "...WARNING: could not set the given mode (%d)\n", mode );
+ return qfalse;
+ default:
+ break;
+ }
+ return qtrue;
+}
+
+/*
+** GLW_SetMode
+*/
+int GLW_SetMode( const char *drivername, int mode, qboolean fullscreen )
+{
+ int attrib[] = {
+ GLX_RGBA, // 0
+ GLX_RED_SIZE, 4, // 1, 2
+ GLX_GREEN_SIZE, 4, // 3, 4
+ GLX_BLUE_SIZE, 4, // 5, 6
+ GLX_DOUBLEBUFFER, // 7
+ GLX_DEPTH_SIZE, 1, // 8, 9
+ GLX_STENCIL_SIZE, 1, // 10, 11
+ None
+ };
+ // these match in the array
+#define ATTR_RED_IDX 2
+#define ATTR_GREEN_IDX 4
+#define ATTR_BLUE_IDX 6
+#define ATTR_DEPTH_IDX 9
+#define ATTR_STENCIL_IDX 11
+ Window root;
+ XVisualInfo *visinfo;
+ XSetWindowAttributes attr;
+ XSizeHints sizehints;
+ unsigned long mask;
+ int colorbits, depthbits, stencilbits;
+ int tcolorbits, tdepthbits, tstencilbits;
+ int dga_MajorVersion, dga_MinorVersion;
+ int actualWidth, actualHeight;
+ int i;
+ const char* glstring; // bk001130 - from cvs1.17 (mkv)
+
+ ri.Printf( PRINT_ALL, "Initializing OpenGL display\n");
+
+ ri.Printf (PRINT_ALL, "...setting mode %d:", mode );
+
+ if ( !R_GetModeInfo( &glConfig.vidWidth, &glConfig.vidHeight, &glConfig.windowAspect, mode ) )
+ {
+ ri.Printf( PRINT_ALL, " invalid mode\n" );
+ return RSERR_INVALID_MODE;
+ }
+ ri.Printf( PRINT_ALL, " %d %d\n", glConfig.vidWidth, glConfig.vidHeight);
+
+ if (!(dpy = XOpenDisplay(NULL)))
+ {
+ fprintf(stderr, "Error couldn't open the X display\n");
+ return RSERR_INVALID_MODE;
+ }
+
+ scrnum = DefaultScreen(dpy);
+ root = RootWindow(dpy, scrnum);
+
+ actualWidth = glConfig.vidWidth;
+ actualHeight = glConfig.vidHeight;
+
+ // Get video mode list
+ if (!XF86VidModeQueryVersion(dpy, &vidmode_MajorVersion, &vidmode_MinorVersion))
+ {
+ vidmode_ext = qfalse;
+ } else
+ {
+ ri.Printf(PRINT_ALL, "Using XFree86-VidModeExtension Version %d.%d\n",
+ vidmode_MajorVersion, vidmode_MinorVersion);
+ vidmode_ext = qtrue;
+ }
+
+ // Check for DGA
+ dga_MajorVersion = 0, dga_MinorVersion = 0;
+ if (in_dgamouse->value)
+ {
+ if (!XF86DGAQueryVersion(dpy, &dga_MajorVersion, &dga_MinorVersion))
+ {
+ // unable to query, probalby not supported
+ ri.Printf( PRINT_ALL, "Failed to detect XF86DGA Mouse\n" );
+ ri.Cvar_Set( "in_dgamouse", "0" );
+ } else
+ {
+ ri.Printf( PRINT_ALL, "XF86DGA Mouse (Version %d.%d) initialized\n",
+ dga_MajorVersion, dga_MinorVersion);
+ }
+ }
+
+ if (vidmode_ext)
+ {
+ int best_fit, best_dist, dist, x, y;
+
+ XF86VidModeGetAllModeLines(dpy, scrnum, &num_vidmodes, &vidmodes);
+
+ // Are we going fullscreen? If so, let's change video mode
+ if (fullscreen)
+ {
+ best_dist = 9999999;
+ best_fit = -1;
+
+ for (i = 0; i < num_vidmodes; i++)
+ {
+ if (glConfig.vidWidth > vidmodes[i]->hdisplay ||
+ glConfig.vidHeight > vidmodes[i]->vdisplay)
+ continue;
+
+ x = glConfig.vidWidth - vidmodes[i]->hdisplay;
+ y = glConfig.vidHeight - vidmodes[i]->vdisplay;
+ dist = (x * x) + (y * y);
+ if (dist < best_dist)
+ {
+ best_dist = dist;
+ best_fit = i;
+ }
+ }
+
+ if (best_fit != -1)
+ {
+ actualWidth = vidmodes[best_fit]->hdisplay;
+ actualHeight = vidmodes[best_fit]->vdisplay;
+
+ // change to the mode
+ XF86VidModeSwitchToMode(dpy, scrnum, vidmodes[best_fit]);
+ vidmode_active = qtrue;
+
+ // Move the viewport to top left
+ XF86VidModeSetViewPort(dpy, scrnum, 0, 0);
+
+ ri.Printf(PRINT_ALL, "XFree86-VidModeExtension Activated at %dx%d\n",
+ actualWidth, actualHeight);
+
+ } else
+ {
+ fullscreen = 0;
+ ri.Printf(PRINT_ALL, "XFree86-VidModeExtension: No acceptable modes found\n");
+ }
+ } else
+ {
+ ri.Printf(PRINT_ALL, "XFree86-VidModeExtension: Ignored on non-fullscreen/Voodoo\n");
+ }
+ }
+
+
+ if (!r_colorbits->value)
+ colorbits = 24;
+ else
+ colorbits = r_colorbits->value;
+
+ if ( !Q_stricmp( r_glDriver->string, _3DFX_DRIVER_NAME ) )
+ colorbits = 16;
+
+ if (!r_depthbits->value)
+ depthbits = 24;
+ else
+ depthbits = r_depthbits->value;
+ stencilbits = r_stencilbits->value;
+
+ for (i = 0; i < 16; i++)
+ {
+ // 0 - default
+ // 1 - minus colorbits
+ // 2 - minus depthbits
+ // 3 - minus stencil
+ if ((i % 4) == 0 && i)
+ {
+ // one pass, reduce
+ switch (i / 4)
+ {
+ case 2 :
+ if (colorbits == 24)
+ colorbits = 16;
+ break;
+ case 1 :
+ if (depthbits == 24)
+ depthbits = 16;
+ else if (depthbits == 16)
+ depthbits = 8;
+ case 3 :
+ if (stencilbits == 24)
+ stencilbits = 16;
+ else if (stencilbits == 16)
+ stencilbits = 8;
+ }
+ }
+
+ tcolorbits = colorbits;
+ tdepthbits = depthbits;
+ tstencilbits = stencilbits;
+
+ if ((i % 4) == 3)
+ { // reduce colorbits
+ if (tcolorbits == 24)
+ tcolorbits = 16;
+ }
+
+ if ((i % 4) == 2)
+ { // reduce depthbits
+ if (tdepthbits == 24)
+ tdepthbits = 16;
+ else if (tdepthbits == 16)
+ tdepthbits = 8;
+ }
+
+ if ((i % 4) == 1)
+ { // reduce stencilbits
+ if (tstencilbits == 24)
+ tstencilbits = 16;
+ else if (tstencilbits == 16)
+ tstencilbits = 8;
+ else
+ tstencilbits = 0;
+ }
+
+ if (tcolorbits == 24)
+ {
+ attrib[ATTR_RED_IDX] = 8;
+ attrib[ATTR_GREEN_IDX] = 8;
+ attrib[ATTR_BLUE_IDX] = 8;
+ } else
+ {
+ // must be 16 bit
+ attrib[ATTR_RED_IDX] = 4;
+ attrib[ATTR_GREEN_IDX] = 4;
+ attrib[ATTR_BLUE_IDX] = 4;
+ }
+
+ attrib[ATTR_DEPTH_IDX] = tdepthbits; // default to 24 depth
+ attrib[ATTR_STENCIL_IDX] = tstencilbits;
+
+ visinfo = qglXChooseVisual(dpy, scrnum, attrib);
+ if (!visinfo)
+ {
+ continue;
+ }
+
+ ri.Printf( PRINT_ALL, "Using %d/%d/%d Color bits, %d depth, %d stencil display.\n",
+ attrib[ATTR_RED_IDX], attrib[ATTR_GREEN_IDX], attrib[ATTR_BLUE_IDX],
+ attrib[ATTR_DEPTH_IDX], attrib[ATTR_STENCIL_IDX]);
+
+ glConfig.colorBits = tcolorbits;
+ glConfig.depthBits = tdepthbits;
+ glConfig.stencilBits = tstencilbits;
+ break;
+ }
+
+ if (!visinfo)
+ {
+ ri.Printf( PRINT_ALL, "Couldn't get a visual\n" );
+ return RSERR_INVALID_MODE;
+ }
+
+ /* window attributes */
+ attr.background_pixel = BlackPixel(dpy, scrnum);
+ attr.border_pixel = 0;
+ attr.colormap = XCreateColormap(dpy, root, visinfo->visual, AllocNone);
+ attr.event_mask = X_MASK;
+ if (vidmode_active)
+ {
+ mask = CWBackPixel | CWColormap | CWSaveUnder | CWBackingStore |
+ CWEventMask | CWOverrideRedirect;
+ attr.override_redirect = True;
+ attr.backing_store = NotUseful;
+ attr.save_under = False;
+ } else
+ mask = CWBackPixel | CWBorderPixel | CWColormap | CWEventMask;
+
+ win = XCreateWindow(dpy, root, 0, 0,
+ actualWidth, actualHeight,
+ 0, visinfo->depth, InputOutput,
+ visinfo->visual, mask, &attr);
+
+ XStoreName( dpy, win, WINDOW_CLASS_NAME );
+
+ /* GH: Don't let the window be resized */
+ sizehints.flags = PMinSize | PMaxSize;
+ sizehints.min_width = sizehints.max_width = actualWidth;
+ sizehints.min_height = sizehints.max_height = actualHeight;
+
+ XSetWMNormalHints( dpy, win, &sizehints );
+
+ XMapWindow( dpy, win );
+
+ if (vidmode_active)
+ XMoveWindow(dpy, win, 0, 0);
+
+ XFlush(dpy);
+ XSync(dpy,False); // bk001130 - from cvs1.17 (mkv)
+ ctx = qglXCreateContext(dpy, visinfo, NULL, True);
+ XSync(dpy,False); // bk001130 - from cvs1.17 (mkv)
+
+ /* GH: Free the visinfo after we're done with it */
+ XFree( visinfo );
+
+ qglXMakeCurrent(dpy, win, ctx);
+
+ // bk001130 - from cvs1.17 (mkv)
+ glstring = qglGetString (GL_RENDERER);
+ ri.Printf( PRINT_ALL, "GL_RENDERER: %s\n", glstring );
+
+ // bk010122 - new software token (Indirect)
+ if ( !Q_stricmp( glstring, "Mesa X11")
+ || !Q_stricmp( glstring, "Mesa GLX Indirect") )
+ {
+ if ( !r_allowSoftwareGL->integer )
+ {
+ ri.Printf( PRINT_ALL, "\n\n***********************************************************\n" );
+ ri.Printf( PRINT_ALL, " You are using software Mesa (no hardware acceleration)! \n" );
+ ri.Printf( PRINT_ALL, " Driver DLL used: %s\n", drivername );
+ ri.Printf( PRINT_ALL, " If this is intentional, add\n" );
+ ri.Printf( PRINT_ALL, " \"+set r_allowSoftwareGL 1\"\n" );
+ ri.Printf( PRINT_ALL, " to the command line when starting the game.\n" );
+ ri.Printf( PRINT_ALL, "***********************************************************\n");
+ GLimp_Shutdown( );
+ return RSERR_INVALID_MODE;
+ } else
+ {
+ ri.Printf( PRINT_ALL, "...using software Mesa (r_allowSoftwareGL==1).\n" );
+ }
+ }
+
+ return RSERR_OK;
+}
+
+/*
+** GLW_InitExtensions
+*/
+static void GLW_InitExtensions( void )
+{
+ if ( !r_allowExtensions->integer )
+ {
+ ri.Printf( PRINT_ALL, "*** IGNORING OPENGL EXTENSIONS ***\n" );
+ return;
+ }
+
+ ri.Printf( PRINT_ALL, "Initializing OpenGL extensions\n" );
+
+ // GL_S3_s3tc
+ if ( Q_stristr( glConfig.extensions_string, "GL_S3_s3tc" ) )
+ {
+ if ( r_ext_compressed_textures->value )
+ {
+ glConfig.textureCompression = TC_S3TC;
+ ri.Printf( PRINT_ALL, "...using GL_S3_s3tc\n" );
+ } else
+ {
+ glConfig.textureCompression = TC_NONE;
+ ri.Printf( PRINT_ALL, "...ignoring GL_S3_s3tc\n" );
+ }
+ } else
+ {
+ glConfig.textureCompression = TC_NONE;
+ ri.Printf( PRINT_ALL, "...GL_S3_s3tc not found\n" );
+ }
+
+ // GL_EXT_texture_env_add
+ glConfig.textureEnvAddAvailable = qfalse;
+ if ( Q_stristr( glConfig.extensions_string, "EXT_texture_env_add" ) )
+ {
+ if ( r_ext_texture_env_add->integer )
+ {
+ glConfig.textureEnvAddAvailable = qtrue;
+ ri.Printf( PRINT_ALL, "...using GL_EXT_texture_env_add\n" );
+ } else
+ {
+ glConfig.textureEnvAddAvailable = qfalse;
+ ri.Printf( PRINT_ALL, "...ignoring GL_EXT_texture_env_add\n" );
+ }
+ } else
+ {
+ ri.Printf( PRINT_ALL, "...GL_EXT_texture_env_add not found\n" );
+ }
+
+ // GL_ARB_multitexture
+ qglMultiTexCoord2fARB = NULL;
+ qglActiveTextureARB = NULL;
+ qglClientActiveTextureARB = NULL;
+ if ( Q_stristr( glConfig.extensions_string, "GL_ARB_multitexture" ) )
+ {
+ if ( r_ext_multitexture->value )
+ {
+ qglMultiTexCoord2fARB = ( PFNGLMULTITEXCOORD2FARBPROC ) dlsym( glw_state.OpenGLLib, "glMultiTexCoord2fARB" );
+ qglActiveTextureARB = ( PFNGLACTIVETEXTUREARBPROC ) dlsym( glw_state.OpenGLLib, "glActiveTextureARB" );
+ qglClientActiveTextureARB = ( PFNGLCLIENTACTIVETEXTUREARBPROC ) dlsym( glw_state.OpenGLLib, "glClientActiveTextureARB" );
+
+ if ( qglActiveTextureARB )
+ {
+ qglGetIntegerv( GL_MAX_ACTIVE_TEXTURES_ARB, &glConfig.maxActiveTextures );
+
+ if ( glConfig.maxActiveTextures > 1 )
+ {
+ ri.Printf( PRINT_ALL, "...using GL_ARB_multitexture\n" );
+ } else
+ {
+ qglMultiTexCoord2fARB = NULL;
+ qglActiveTextureARB = NULL;
+ qglClientActiveTextureARB = NULL;
+ ri.Printf( PRINT_ALL, "...not using GL_ARB_multitexture, < 2 texture units\n" );
+ }
+ }
+ } else
+ {
+ ri.Printf( PRINT_ALL, "...ignoring GL_ARB_multitexture\n" );
+ }
+ } else
+ {
+ ri.Printf( PRINT_ALL, "...GL_ARB_multitexture not found\n" );
+ }
+
+ // GL_EXT_compiled_vertex_array
+ if ( Q_stristr( glConfig.extensions_string, "GL_EXT_compiled_vertex_array" ) )
+ {
+ if ( r_ext_compiled_vertex_array->value )
+ {
+ ri.Printf( PRINT_ALL, "...using GL_EXT_compiled_vertex_array\n" );
+ qglLockArraysEXT = ( void ( APIENTRY * )( int, int ) ) dlsym( glw_state.OpenGLLib, "glLockArraysEXT" );
+ qglUnlockArraysEXT = ( void ( APIENTRY * )( void ) ) dlsym( glw_state.OpenGLLib, "glUnlockArraysEXT" );
+ if (!qglLockArraysEXT || !qglUnlockArraysEXT)
+ {
+ ri.Error (ERR_FATAL, "bad getprocaddress");
+ }
+ } else
+ {
+ ri.Printf( PRINT_ALL, "...ignoring GL_EXT_compiled_vertex_array\n" );
+ }
+ } else
+ {
+ ri.Printf( PRINT_ALL, "...GL_EXT_compiled_vertex_array not found\n" );
+ }
+
+}
+
+static void GLW_InitGamma()
+{
+ /* Minimum extension version required */
+ #define GAMMA_MINMAJOR 2
+ #define GAMMA_MINMINOR 0
+
+ glConfig.deviceSupportsGamma = qfalse;
+
+ if (vidmode_ext)
+ {
+ if (vidmode_MajorVersion < GAMMA_MINMAJOR ||
+ (vidmode_MajorVersion == GAMMA_MINMAJOR && vidmode_MinorVersion < GAMMA_MINMINOR)) {
+ ri.Printf( PRINT_ALL, "XF86 Gamma extension not supported in this version\n");
+ return;
+ }
+ XF86VidModeGetGamma(dpy, scrnum, &vidmode_InitialGamma);
+ ri.Printf( PRINT_ALL, "XF86 Gamma extension initialized\n");
+ glConfig.deviceSupportsGamma = qtrue;
+ }
+}
+
+/*
+** GLW_LoadOpenGL
+**
+** GLimp_win.c internal function that that attempts to load and use
+** a specific OpenGL DLL.
+*/
+static qboolean GLW_LoadOpenGL( const char *name )
+{
+ qboolean fullscreen;
+
+ ri.Printf( PRINT_ALL, "...loading %s: ", name );
+
+ // disable the 3Dfx splash screen and set gamma
+ // we do this all the time, but it shouldn't hurt anything
+ // on non-3Dfx stuff
+ putenv("FX_GLIDE_NO_SPLASH=0");
+
+ // Mesa VooDoo hacks
+ putenv("MESA_GLX_FX=fullscreen\n");
+
+ // load the QGL layer
+ if ( QGL_Init( name ) )
+ {
+ fullscreen = r_fullscreen->integer;
+
+ // create the window and set up the context
+ if ( !GLW_StartDriverAndSetMode( name, r_mode->integer, fullscreen ) )
+ {
+ if (r_mode->integer != 3)
+ {
+ if ( !GLW_StartDriverAndSetMode( name, 3, fullscreen ) )
+ {
+ goto fail;
+ }
+ } else
+ goto fail;
+ }
+
+ return qtrue;
+ } else
+ {
+ ri.Printf( PRINT_ALL, "failed\n" );
+ }
+ fail:
+
+ QGL_Shutdown();
+
+ return qfalse;
+}
+
+/*
+** XErrorHandler
+** the default X error handler exits the application
+** I found out that on some hosts some operations would raise X errors (GLXUnsupportedPrivateRequest)
+** but those don't seem to be fatal .. so the default would be to just ignore them
+** our implementation mimics the default handler behaviour (not completely cause I'm lazy)
+*/
+int qXErrorHandler(Display *dpy, XErrorEvent *ev)
+{
+ static char buf[1024];
+ XGetErrorText(dpy, ev->error_code, buf, 1024);
+ ri.Printf( PRINT_ALL, "X Error of failed request: %s\n", buf);
+ ri.Printf( PRINT_ALL, " Major opcode of failed request: %d\n", ev->request_code, buf);
+ ri.Printf( PRINT_ALL, " Minor opcode of failed request: %d\n", ev->minor_code);
+ ri.Printf( PRINT_ALL, " Serial number of failed request: %d\n", ev->serial);
+ return 0;
+}
+
+/*
+** GLimp_Init
+**
+** This routine is responsible for initializing the OS specific portions
+** of OpenGL.
+*/
+void GLimp_Init( void )
+{
+ qboolean attemptedlibGL = qfalse;
+ qboolean attempted3Dfx = qfalse;
+ qboolean success = qfalse;
+ char buf[1024];
+ cvar_t *lastValidRenderer = ri.Cvar_Get( "r_lastValidRenderer", "(uninitialized)", CVAR_ARCHIVE );
+
+ // guarded, as this is only relevant to SMP renderer thread
+#ifdef SMP
+ if (!XInitThreads())
+ {
+ Com_Printf("GLimp_Init() - XInitThreads() failed, disabling r_smp\n");
+ ri.Cvar_Set( "r_smp", "0" );
+ }
+#endif
+
+ r_allowSoftwareGL = ri.Cvar_Get( "r_allowSoftwareGL", "0", CVAR_LATCH );
+
+ r_previousglDriver = ri.Cvar_Get( "r_previousglDriver", "", CVAR_ROM );
+
+ InitSig();
+
+ // Hack here so that if the UI
+ if ( *r_previousglDriver->string )
+ {
+ // The UI changed it on us, hack it back
+ // This means the renderer can't be changed on the fly
+ ri.Cvar_Set( "r_glDriver", r_previousglDriver->string );
+ }
+
+ // set up our custom error handler for X failures
+ XSetErrorHandler(&qXErrorHandler);
+
+ //
+ // load and initialize the specific OpenGL driver
+ //
+ if ( !GLW_LoadOpenGL( r_glDriver->string ) )
+ {
+ if ( !Q_stricmp( r_glDriver->string, OPENGL_DRIVER_NAME ) )
+ {
+ attemptedlibGL = qtrue;
+ } else if ( !Q_stricmp( r_glDriver->string, _3DFX_DRIVER_NAME ) )
+ {
+ attempted3Dfx = qtrue;
+ }
+
+ #if 0
+ // TTimo
+ // https://zerowing.idsoftware.com/bugzilla/show_bug.cgi?id=455
+ // old legacy load code, was confusing people who had a bad OpenGL setup
+ if ( !attempted3Dfx && !success )
+ {
+ attempted3Dfx = qtrue;
+ if ( GLW_LoadOpenGL( _3DFX_DRIVER_NAME ) )
+ {
+ ri.Cvar_Set( "r_glDriver", _3DFX_DRIVER_NAME );
+ r_glDriver->modified = qfalse;
+ success = qtrue;
+ }
+ }
+ #endif
+
+ // try ICD before trying 3Dfx standalone driver
+ if ( !attemptedlibGL && !success )
+ {
+ attemptedlibGL = qtrue;
+ if ( GLW_LoadOpenGL( OPENGL_DRIVER_NAME ) )
+ {
+ ri.Cvar_Set( "r_glDriver", OPENGL_DRIVER_NAME );
+ r_glDriver->modified = qfalse;
+ success = qtrue;
+ }
+ }
+
+ if (!success)
+ ri.Error( ERR_FATAL, "GLimp_Init() - could not load OpenGL subsystem\n" );
+
+ }
+
+ // Save it in case the UI stomps it
+ ri.Cvar_Set( "r_previousglDriver", r_glDriver->string );
+
+ // This values force the UI to disable driver selection
+ glConfig.driverType = GLDRV_ICD;
+ glConfig.hardwareType = GLHW_GENERIC;
+
+ // get our config strings
+ Q_strncpyz( glConfig.vendor_string, qglGetString (GL_VENDOR), sizeof( glConfig.vendor_string ) );
+ Q_strncpyz( glConfig.renderer_string, qglGetString (GL_RENDERER), sizeof( glConfig.renderer_string ) );
+ if (*glConfig.renderer_string && glConfig.renderer_string[strlen(glConfig.renderer_string) - 1] == '\n')
+ glConfig.renderer_string[strlen(glConfig.renderer_string) - 1] = 0;
+ Q_strncpyz( glConfig.version_string, qglGetString (GL_VERSION), sizeof( glConfig.version_string ) );
+ Q_strncpyz( glConfig.extensions_string, qglGetString (GL_EXTENSIONS), sizeof( glConfig.extensions_string ) );
+
+ //
+ // chipset specific configuration
+ //
+ strcpy( buf, glConfig.renderer_string );
+ strlwr( buf );
+
+ //
+ // NOTE: if changing cvars, do it within this block. This allows them
+ // to be overridden when testing driver fixes, etc. but only sets
+ // them to their default state when the hardware is first installed/run.
+ //
+ if ( Q_stricmp( lastValidRenderer->string, glConfig.renderer_string ) )
+ {
+ glConfig.hardwareType = GLHW_GENERIC;
+
+ ri.Cvar_Set( "r_textureMode", "GL_LINEAR_MIPMAP_NEAREST" );
+
+ // VOODOO GRAPHICS w/ 2MB
+ if ( Q_stristr( buf, "voodoo graphics/1 tmu/2 mb" ) )
+ {
+ ri.Cvar_Set( "r_picmip", "2" );
+ ri.Cvar_Get( "r_picmip", "1", CVAR_ARCHIVE | CVAR_LATCH );
+ } else
+ {
+ ri.Cvar_Set( "r_picmip", "1" );
+
+ if ( Q_stristr( buf, "rage 128" ) || Q_stristr( buf, "rage128" ) )
+ {
+ ri.Cvar_Set( "r_finish", "0" );
+ }
+ // Savage3D and Savage4 should always have trilinear enabled
+ else if ( Q_stristr( buf, "savage3d" ) || Q_stristr( buf, "s3 savage4" ) )
+ {
+ ri.Cvar_Set( "r_texturemode", "GL_LINEAR_MIPMAP_LINEAR" );
+ }
+ }
+ }
+
+ //
+ // this is where hardware specific workarounds that should be
+ // detected/initialized every startup should go.
+ //
+ if ( Q_stristr( buf, "banshee" ) || Q_stristr( buf, "Voodoo_Graphics" ) )
+ {
+ glConfig.hardwareType = GLHW_3DFX_2D3D;
+ } else if ( Q_stristr( buf, "rage pro" ) || Q_stristr( buf, "RagePro" ) )
+ {
+ glConfig.hardwareType = GLHW_RAGEPRO;
+ } else if ( Q_stristr( buf, "permedia2" ) )
+ {
+ glConfig.hardwareType = GLHW_PERMEDIA2;
+ } else if ( Q_stristr( buf, "riva 128" ) )
+ {
+ glConfig.hardwareType = GLHW_RIVA128;
+ } else if ( Q_stristr( buf, "riva tnt " ) )
+ {
+ }
+
+ ri.Cvar_Set( "r_lastValidRenderer", glConfig.renderer_string );
+
+ // initialize extensions
+ GLW_InitExtensions();
+ GLW_InitGamma();
+
+ InitSig(); // not clear why this is at begin & end of function
+
+ return;
+}
+
+
+/*
+** GLimp_EndFrame
+**
+** Responsible for doing a swapbuffers and possibly for other stuff
+** as yet to be determined. Probably better not to make this a GLimp
+** function and instead do a call to GLimp_SwapBuffers.
+*/
+void GLimp_EndFrame (void)
+{
+ // don't flip if drawing to front buffer
+ if ( stricmp( r_drawBuffer->string, "GL_FRONT" ) != 0 )
+ {
+ qglXSwapBuffers(dpy, win);
+ }
+
+ // check logging
+ QGL_EnableLogging( (qboolean)r_logFile->integer ); // bk001205 - was ->value
+}
+
+#ifdef SMP
+/*
+===========================================================
+
+SMP acceleration
+
+===========================================================
+*/
+
+static pthread_mutex_t smpMutex = PTHREAD_MUTEX_INITIALIZER;
+
+static pthread_cond_t renderCommandsEvent = PTHREAD_COND_INITIALIZER;
+static pthread_cond_t renderCompletedEvent = PTHREAD_COND_INITIALIZER;
+
+static void (*glimpRenderThread)( void );
+
+static void *GLimp_RenderThreadWrapper( void *arg )
+{
+ Com_Printf( "Render thread starting\n" );
+
+ glimpRenderThread();
+
+ qglXMakeCurrent( dpy, None, NULL );
+
+ Com_Printf( "Render thread terminating\n" );
+
+ return arg;
+}
+
+qboolean GLimp_SpawnRenderThread( void (*function)( void ) )
+{
+ pthread_t renderThread;
+ int ret;
+
+ pthread_mutex_init( &smpMutex, NULL );
+
+ pthread_cond_init( &renderCommandsEvent, NULL );
+ pthread_cond_init( &renderCompletedEvent, NULL );
+
+ glimpRenderThread = function;
+
+ ret = pthread_create( &renderThread,
+ NULL, // attributes
+ GLimp_RenderThreadWrapper,
+ NULL ); // argument
+ if ( ret ) {
+ ri.Printf( PRINT_ALL, "pthread_create returned %d: %s", ret, strerror( ret ) );
+ return qfalse;
+ } else {
+ ret = pthread_detach( renderThread );
+ if ( ret ) {
+ ri.Printf( PRINT_ALL, "pthread_detach returned %d: %s", ret, strerror( ret ) );
+ }
+ }
+
+ return qtrue;
+}
+
+static volatile void *smpData = NULL;
+static volatile qboolean smpDataReady;
+
+void *GLimp_RendererSleep( void )
+{
+ void *data;
+
+ qglXMakeCurrent( dpy, None, NULL );
+
+ pthread_mutex_lock( &smpMutex );
+ {
+ smpData = NULL;
+ smpDataReady = qfalse;
+
+ // after this, the front end can exit GLimp_FrontEndSleep
+ pthread_cond_signal( &renderCompletedEvent );
+
+ while ( !smpDataReady ) {
+ pthread_cond_wait( &renderCommandsEvent, &smpMutex );
+ }
+
+ data = (void *)smpData;
+ }
+ pthread_mutex_unlock( &smpMutex );
+
+ qglXMakeCurrent( dpy, win, ctx );
+
+ return data;
+}
+
+void GLimp_FrontEndSleep( void )
+{
+ pthread_mutex_lock( &smpMutex );
+ {
+ while ( smpData ) {
+ pthread_cond_wait( &renderCompletedEvent, &smpMutex );
+ }
+ }
+ pthread_mutex_unlock( &smpMutex );
+
+ qglXMakeCurrent( dpy, win, ctx );
+}
+
+void GLimp_WakeRenderer( void *data )
+{
+ qglXMakeCurrent( dpy, None, NULL );
+
+ pthread_mutex_lock( &smpMutex );
+ {
+ assert( smpData == NULL );
+ smpData = data;
+ smpDataReady = qtrue;
+
+ // after this, the renderer can continue through GLimp_RendererSleep
+ pthread_cond_signal( &renderCommandsEvent );
+ }
+ pthread_mutex_unlock( &smpMutex );
+}
+
+#else
+
+void GLimp_RenderThreadWrapper( void *stub ) {}
+qboolean GLimp_SpawnRenderThread( void (*function)( void ) ) {
+ ri.Printf( PRINT_WARNING, "ERROR: SMP support was disabled at compile time\n");
+ return qfalse;
+}
+void *GLimp_RendererSleep( void ) {
+ return NULL;
+}
+void GLimp_FrontEndSleep( void ) {}
+void GLimp_WakeRenderer( void *data ) {}
+
+#endif
+
+/*****************************************************************************/
+/* MOUSE */
+/*****************************************************************************/
+
+void IN_Init(void) {
+ Com_Printf ("\n------- Input Initialization -------\n");
+ // mouse variables
+ in_mouse = Cvar_Get ("in_mouse", "1", CVAR_ARCHIVE);
+ in_dgamouse = Cvar_Get ("in_dgamouse", "1", CVAR_ARCHIVE);
+
+ // turn on-off sub-frame timing of X events
+ in_subframe = Cvar_Get ("in_subframe", "1", CVAR_ARCHIVE);
+
+ // developer feature, allows to break without loosing mouse pointer
+ in_nograb = Cvar_Get ("in_nograb", "0", 0);
+
+ // bk001130 - from cvs.17 (mkv), joystick variables
+ in_joystick = Cvar_Get ("in_joystick", "0", CVAR_ARCHIVE|CVAR_LATCH);
+ // bk001130 - changed this to match win32
+ in_joystickDebug = Cvar_Get ("in_debugjoystick", "0", CVAR_TEMP);
+ joy_threshold = Cvar_Get ("joy_threshold", "0.15", CVAR_ARCHIVE); // FIXME: in_joythreshold
+
+ if (in_mouse->value)
+ mouse_avail = qtrue;
+ else
+ mouse_avail = qfalse;
+
+ IN_StartupJoystick( ); // bk001130 - from cvs1.17 (mkv)
+ Com_Printf ("------------------------------------\n");
+}
+
+void IN_Shutdown(void)
+{
+ mouse_avail = qfalse;
+}
+
+void IN_Frame (void) {
+
+ // bk001130 - from cvs 1.17 (mkv)
+ IN_JoyMove(); // FIXME: disable if on desktop?
+
+ if ( cls.keyCatchers & KEYCATCH_CONSOLE )
+ {
+ // temporarily deactivate if not in the game and
+ // running on the desktop
+ // voodoo always counts as full screen
+ if (Cvar_VariableValue ("r_fullscreen") == 0
+ && strcmp( Cvar_VariableString("r_glDriver"), _3DFX_DRIVER_NAME ) )
+ {
+ IN_DeactivateMouse ();
+ return;
+ }
+ }
+
+ IN_ActivateMouse();
+}
+
+void IN_Activate(void)
+{
+}
+
+// bk001130 - cvs1.17 joystick code (mkv) was here, no linux_joystick.c
+
+void Sys_SendKeyEvents (void) {
+ // XEvent event; // bk001204 - unused
+
+ if (!dpy)
+ return;
+ HandleEvents();
+}
+
+
+// bk010216 - added stubs for non-Linux UNIXes here
+// FIXME - use NO_JOYSTICK or something else generic
+
+#if defined( __FreeBSD__ ) // rb010123
+void IN_StartupJoystick( void ) {}
+void IN_JoyMove( void ) {}
+#endif
diff --git a/code/unix/linux_joystick.c b/code/unix/linux_joystick.c
new file mode 100755
index 0000000..f287e8b
--- /dev/null
+++ b/code/unix/linux_joystick.c
@@ -0,0 +1,207 @@
+/*
+===========================================================================
+Copyright (C) 1999-2005 Id Software, Inc.
+
+This file is part of Quake III Arena source code.
+
+Quake III Arena source code is free software; you can redistribute it
+and/or modify it under the terms of the GNU General Public License as
+published by the Free Software Foundation; either version 2 of the License,
+or (at your option) any later version.
+
+Quake III Arena source code is distributed in the hope that it will be
+useful, but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with Foobar; if not, write to the Free Software
+Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+===========================================================================
+*/
+/*
+** linux_joystick.c
+**
+** This file contains ALL Linux specific stuff having to do with the
+** Joystick input. When a port is being made the following functions
+** must be implemented by the port:
+**
+** Authors: mkv, bk
+**
+*/
+
+#include <linux/joystick.h>
+#include <sys/types.h>
+#include <fcntl.h>
+#include <sys/ioctl.h>
+#include <unistd.h> // bk001204
+
+
+#include "../client/client.h"
+#include "linux_local.h"
+
+/* We translate axes movement into keypresses. */
+int joy_keys[16] = {
+ K_LEFTARROW, K_RIGHTARROW,
+ K_UPARROW, K_DOWNARROW,
+ K_JOY16, K_JOY17,
+ K_JOY18, K_JOY19,
+ K_JOY20, K_JOY21,
+ K_JOY22, K_JOY23,
+
+ K_JOY24, K_JOY25,
+ K_JOY26, K_JOY27
+};
+
+/* Our file descriptor for the joystick device. */
+static int joy_fd = -1;
+
+
+// bk001130 - from linux_glimp.c
+extern cvar_t * in_joystick;
+extern cvar_t * in_joystickDebug;
+extern cvar_t * joy_threshold;
+
+
+/**********************************************/
+/* Joystick routines. */
+/**********************************************/
+// bk001130 - from cvs1.17 (mkv), removed from linux_glimp.c
+void IN_StartupJoystick( void )
+{
+ int i = 0;
+
+ joy_fd = -1;
+
+ if( !in_joystick->integer ) {
+ Com_Printf( "Joystick is not active.\n" );
+ return;
+ }
+
+ for( i = 0; i < 4; i++ ) {
+ char filename[PATH_MAX];
+
+ snprintf( filename, PATH_MAX, "/dev/js%d", i );
+
+ joy_fd = open( filename, O_RDONLY | O_NONBLOCK );
+
+ if( joy_fd != -1 ) {
+ struct js_event event;
+ char axes = 0;
+ char buttons = 0;
+ char name[128];
+ int n = -1;
+
+ Com_Printf( "Joystick %s found\n", filename );
+
+ /* Get rid of initialization messages. */
+ do {
+ n = read( joy_fd, &event, sizeof( event ) );
+
+ if( n == -1 ) {
+ break;
+ }
+
+ } while( ( event.type & JS_EVENT_INIT ) );
+
+ /* Get joystick statistics. */
+ ioctl( joy_fd, JSIOCGAXES, &axes );
+ ioctl( joy_fd, JSIOCGBUTTONS, &buttons );
+
+ if( ioctl( joy_fd, JSIOCGNAME( sizeof( name ) ), name ) < 0 ) {
+ strncpy( name, "Unknown", sizeof( name ) );
+ }
+
+ Com_Printf( "Name: %s\n", name );
+ Com_Printf( "Axes: %d\n", axes );
+ Com_Printf( "Buttons: %d\n", buttons );
+
+ /* Our work here is done. */
+ return;
+ }
+
+ }
+
+ /* No soup for you. */
+ if( joy_fd == -1 ) {
+ Com_Printf( "No joystick found.\n" );
+ return;
+ }
+
+}
+
+void IN_JoyMove( void )
+{
+ /* Store instantaneous joystick state. Hack to get around
+ * event model used in Linux joystick driver.
+ */
+ static int axes_state[16];
+ /* Old bits for Quake-style input compares. */
+ static unsigned int old_axes = 0;
+ /* Our current goodies. */
+ unsigned int axes = 0;
+ int i = 0;
+
+ if( joy_fd == -1 ) {
+ return;
+ }
+
+ /* Empty the queue, dispatching button presses immediately
+ * and updating the instantaneous state for the axes.
+ */
+ do {
+ int n = -1;
+ struct js_event event;
+
+ n = read( joy_fd, &event, sizeof( event ) );
+
+ if( n == -1 ) {
+ /* No error, we're non-blocking. */
+ break;
+ }
+
+ if( event.type & JS_EVENT_BUTTON ) {
+ Sys_QueEvent( 0, SE_KEY, K_JOY1 + event.number, event.value, 0, NULL );
+ } else if( event.type & JS_EVENT_AXIS ) {
+
+ if( event.number >= 16 ) {
+ continue;
+ }
+
+ axes_state[event.number] = event.value;
+ } else {
+ Com_Printf( "Unknown joystick event type\n" );
+ }
+
+ } while( 1 );
+
+
+ /* Translate our instantaneous state to bits. */
+ for( i = 0; i < 16; i++ ) {
+ float f = ( (float) axes_state[i] ) / 32767.0f;
+
+ if( f < -joy_threshold->value ) {
+ axes |= ( 1 << ( i * 2 ) );
+ } else if( f > joy_threshold->value ) {
+ axes |= ( 1 << ( ( i * 2 ) + 1 ) );
+ }
+
+ }
+
+ /* Time to update axes state based on old vs. new. */
+ for( i = 0; i < 16; i++ ) {
+
+ if( ( axes & ( 1 << i ) ) && !( old_axes & ( 1 << i ) ) ) {
+ Sys_QueEvent( 0, SE_KEY, joy_keys[i], qtrue, 0, NULL );
+ }
+
+ if( !( axes & ( 1 << i ) ) && ( old_axes & ( 1 << i ) ) ) {
+ Sys_QueEvent( 0, SE_KEY, joy_keys[i], qfalse, 0, NULL );
+ }
+ }
+
+ /* Save for future generations. */
+ old_axes = axes;
+}
+
+
diff --git a/code/unix/linux_local.h b/code/unix/linux_local.h
new file mode 100755
index 0000000..6d2d619
--- /dev/null
+++ b/code/unix/linux_local.h
@@ -0,0 +1,49 @@
+/*
+===========================================================================
+Copyright (C) 1999-2005 Id Software, Inc.
+
+This file is part of Quake III Arena source code.
+
+Quake III Arena source code is free software; you can redistribute it
+and/or modify it under the terms of the GNU General Public License as
+published by the Free Software Foundation; either version 2 of the License,
+or (at your option) any later version.
+
+Quake III Arena source code is distributed in the hope that it will be
+useful, but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with Foobar; if not, write to the Free Software
+Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+===========================================================================
+*/
+// linux_local.h: Linux-specific Quake3 header file
+
+void Sys_QueEvent( int time, sysEventType_t type, int value, int value2, int ptrLength, void *ptr );
+qboolean Sys_GetPacket ( netadr_t *net_from, msg_t *net_message );
+void Sys_SendKeyEvents (void);
+
+// Input subsystem
+
+void IN_Init (void);
+void IN_Frame (void);
+void IN_Shutdown (void);
+
+
+void IN_JoyMove( void );
+void IN_StartupJoystick( void );
+
+// GL subsystem
+qboolean QGL_Init( const char *dllname );
+void QGL_EnableLogging( qboolean enable );
+void QGL_Shutdown( void );
+
+// bk001130 - win32
+// void IN_JoystickCommands (void);
+
+char *strlwr (char *s);
+
+// signals.c
+void InitSig(void);
diff --git a/code/unix/linux_qgl.c b/code/unix/linux_qgl.c
new file mode 100755
index 0000000..aa49f80
--- /dev/null
+++ b/code/unix/linux_qgl.c
@@ -0,0 +1,4153 @@
+/*
+===========================================================================
+Copyright (C) 1999-2005 Id Software, Inc.
+
+This file is part of Quake III Arena source code.
+
+Quake III Arena source code is free software; you can redistribute it
+and/or modify it under the terms of the GNU General Public License as
+published by the Free Software Foundation; either version 2 of the License,
+or (at your option) any later version.
+
+Quake III Arena source code is distributed in the hope that it will be
+useful, but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with Foobar; if not, write to the Free Software
+Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+===========================================================================
+*/
+/*
+** LINUX_QGL.C
+**
+** This file implements the operating system binding of GL to QGL function
+** pointers. When doing a port of Quake2 you must implement the following
+** two functions:
+**
+** QGL_Init() - loads libraries, assigns function pointers, etc.
+** QGL_Shutdown() - unloads libraries, NULLs function pointers
+*/
+
+// bk001204
+#include <unistd.h>
+#include <sys/types.h>
+
+
+#include <float.h>
+#include "../renderer/tr_local.h"
+#include "unix_glw.h"
+
+// bk001129 - from cvs1.17 (mkv)
+//#if defined(__FX__)
+//#include <GL/fxmesa.h>
+//#endif
+//#include <GL/glx.h> // bk010216 - FIXME: all of the above redundant? renderer/qgl.h
+
+#include <dlfcn.h>
+
+
+// bk001129 - from cvs1.17 (mkv)
+#if defined(__FX__)
+//FX Mesa Functions
+fxMesaContext (*qfxMesaCreateContext)(GLuint win, GrScreenResolution_t, GrScreenRefresh_t, const GLint attribList[]);
+fxMesaContext (*qfxMesaCreateBestContext)(GLuint win, GLint width, GLint height, const GLint attribList[]);
+void (*qfxMesaDestroyContext)(fxMesaContext ctx);
+void (*qfxMesaMakeCurrent)(fxMesaContext ctx);
+fxMesaContext (*qfxMesaGetCurrentContext)(void);
+void (*qfxMesaSwapBuffers)(void);
+#endif
+
+//GLX Functions
+XVisualInfo * (*qglXChooseVisual)( Display *dpy, int screen, int *attribList );
+GLXContext (*qglXCreateContext)( Display *dpy, XVisualInfo *vis, GLXContext shareList, Bool direct );
+void (*qglXDestroyContext)( Display *dpy, GLXContext ctx );
+Bool (*qglXMakeCurrent)( Display *dpy, GLXDrawable drawable, GLXContext ctx);
+void (*qglXCopyContext)( Display *dpy, GLXContext src, GLXContext dst, GLuint mask );
+void (*qglXSwapBuffers)( Display *dpy, GLXDrawable drawable );
+
+void ( APIENTRY * qglAccum )(GLenum op, GLfloat value);
+void ( APIENTRY * qglAlphaFunc )(GLenum func, GLclampf ref);
+GLboolean ( APIENTRY * qglAreTexturesResident )(GLsizei n, const GLuint *textures, GLboolean *residences);
+void ( APIENTRY * qglArrayElement )(GLint i);
+void ( APIENTRY * qglBegin )(GLenum mode);
+void ( APIENTRY * qglBindTexture )(GLenum target, GLuint texture);
+void ( APIENTRY * qglBitmap )(GLsizei width, GLsizei height, GLfloat xorig, GLfloat yorig, GLfloat xmove, GLfloat ymove, const GLubyte *bitmap);
+void ( APIENTRY * qglBlendFunc )(GLenum sfactor, GLenum dfactor);
+void ( APIENTRY * qglCallList )(GLuint list);
+void ( APIENTRY * qglCallLists )(GLsizei n, GLenum type, const GLvoid *lists);
+void ( APIENTRY * qglClear )(GLbitfield mask);
+void ( APIENTRY * qglClearAccum )(GLfloat red, GLfloat green, GLfloat blue, GLfloat alpha);
+void ( APIENTRY * qglClearColor )(GLclampf red, GLclampf green, GLclampf blue, GLclampf alpha);
+void ( APIENTRY * qglClearDepth )(GLclampd depth);
+void ( APIENTRY * qglClearIndex )(GLfloat c);
+void ( APIENTRY * qglClearStencil )(GLint s);
+void ( APIENTRY * qglClipPlane )(GLenum plane, const GLdouble *equation);
+void ( APIENTRY * qglColor3b )(GLbyte red, GLbyte green, GLbyte blue);
+void ( APIENTRY * qglColor3bv )(const GLbyte *v);
+void ( APIENTRY * qglColor3d )(GLdouble red, GLdouble green, GLdouble blue);
+void ( APIENTRY * qglColor3dv )(const GLdouble *v);
+void ( APIENTRY * qglColor3f )(GLfloat red, GLfloat green, GLfloat blue);
+void ( APIENTRY * qglColor3fv )(const GLfloat *v);
+void ( APIENTRY * qglColor3i )(GLint red, GLint green, GLint blue);
+void ( APIENTRY * qglColor3iv )(const GLint *v);
+void ( APIENTRY * qglColor3s )(GLshort red, GLshort green, GLshort blue);
+void ( APIENTRY * qglColor3sv )(const GLshort *v);
+void ( APIENTRY * qglColor3ub )(GLubyte red, GLubyte green, GLubyte blue);
+void ( APIENTRY * qglColor3ubv )(const GLubyte *v);
+void ( APIENTRY * qglColor3ui )(GLuint red, GLuint green, GLuint blue);
+void ( APIENTRY * qglColor3uiv )(const GLuint *v);
+void ( APIENTRY * qglColor3us )(GLushort red, GLushort green, GLushort blue);
+void ( APIENTRY * qglColor3usv )(const GLushort *v);
+void ( APIENTRY * qglColor4b )(GLbyte red, GLbyte green, GLbyte blue, GLbyte alpha);
+void ( APIENTRY * qglColor4bv )(const GLbyte *v);
+void ( APIENTRY * qglColor4d )(GLdouble red, GLdouble green, GLdouble blue, GLdouble alpha);
+void ( APIENTRY * qglColor4dv )(const GLdouble *v);
+void ( APIENTRY * qglColor4f )(GLfloat red, GLfloat green, GLfloat blue, GLfloat alpha);
+void ( APIENTRY * qglColor4fv )(const GLfloat *v);
+void ( APIENTRY * qglColor4i )(GLint red, GLint green, GLint blue, GLint alpha);
+void ( APIENTRY * qglColor4iv )(const GLint *v);
+void ( APIENTRY * qglColor4s )(GLshort red, GLshort green, GLshort blue, GLshort alpha);
+void ( APIENTRY * qglColor4sv )(const GLshort *v);
+void ( APIENTRY * qglColor4ub )(GLubyte red, GLubyte green, GLubyte blue, GLubyte alpha);
+void ( APIENTRY * qglColor4ubv )(const GLubyte *v);
+void ( APIENTRY * qglColor4ui )(GLuint red, GLuint green, GLuint blue, GLuint alpha);
+void ( APIENTRY * qglColor4uiv )(const GLuint *v);
+void ( APIENTRY * qglColor4us )(GLushort red, GLushort green, GLushort blue, GLushort alpha);
+void ( APIENTRY * qglColor4usv )(const GLushort *v);
+void ( APIENTRY * qglColorMask )(GLboolean red, GLboolean green, GLboolean blue, GLboolean alpha);
+void ( APIENTRY * qglColorMaterial )(GLenum face, GLenum mode);
+void ( APIENTRY * qglColorPointer )(GLint size, GLenum type, GLsizei stride, const GLvoid *pointer);
+void ( APIENTRY * qglCopyPixels )(GLint x, GLint y, GLsizei width, GLsizei height, GLenum type);
+void ( APIENTRY * qglCopyTexImage1D )(GLenum target, GLint level, GLenum internalFormat, GLint x, GLint y, GLsizei width, GLint border);
+void ( APIENTRY * qglCopyTexImage2D )(GLenum target, GLint level, GLenum internalFormat, GLint x, GLint y, GLsizei width, GLsizei height, GLint border);
+void ( APIENTRY * qglCopyTexSubImage1D )(GLenum target, GLint level, GLint xoffset, GLint x, GLint y, GLsizei width);
+void ( APIENTRY * qglCopyTexSubImage2D )(GLenum target, GLint level, GLint xoffset, GLint yoffset, GLint x, GLint y, GLsizei width, GLsizei height);
+void ( APIENTRY * qglCullFace )(GLenum mode);
+void ( APIENTRY * qglDeleteLists )(GLuint list, GLsizei range);
+void ( APIENTRY * qglDeleteTextures )(GLsizei n, const GLuint *textures);
+void ( APIENTRY * qglDepthFunc )(GLenum func);
+void ( APIENTRY * qglDepthMask )(GLboolean flag);
+void ( APIENTRY * qglDepthRange )(GLclampd zNear, GLclampd zFar);
+void ( APIENTRY * qglDisable )(GLenum cap);
+void ( APIENTRY * qglDisableClientState )(GLenum array);
+void ( APIENTRY * qglDrawArrays )(GLenum mode, GLint first, GLsizei count);
+void ( APIENTRY * qglDrawBuffer )(GLenum mode);
+void ( APIENTRY * qglDrawElements )(GLenum mode, GLsizei count, GLenum type, const GLvoid *indices);
+void ( APIENTRY * qglDrawPixels )(GLsizei width, GLsizei height, GLenum format, GLenum type, const GLvoid *pixels);
+void ( APIENTRY * qglEdgeFlag )(GLboolean flag);
+void ( APIENTRY * qglEdgeFlagPointer )(GLsizei stride, const GLvoid *pointer);
+void ( APIENTRY * qglEdgeFlagv )(const GLboolean *flag);
+void ( APIENTRY * qglEnable )(GLenum cap);
+void ( APIENTRY * qglEnableClientState )(GLenum array);
+void ( APIENTRY * qglEnd )(void);
+void ( APIENTRY * qglEndList )(void);
+void ( APIENTRY * qglEvalCoord1d )(GLdouble u);
+void ( APIENTRY * qglEvalCoord1dv )(const GLdouble *u);
+void ( APIENTRY * qglEvalCoord1f )(GLfloat u);
+void ( APIENTRY * qglEvalCoord1fv )(const GLfloat *u);
+void ( APIENTRY * qglEvalCoord2d )(GLdouble u, GLdouble v);
+void ( APIENTRY * qglEvalCoord2dv )(const GLdouble *u);
+void ( APIENTRY * qglEvalCoord2f )(GLfloat u, GLfloat v);
+void ( APIENTRY * qglEvalCoord2fv )(const GLfloat *u);
+void ( APIENTRY * qglEvalMesh1 )(GLenum mode, GLint i1, GLint i2);
+void ( APIENTRY * qglEvalMesh2 )(GLenum mode, GLint i1, GLint i2, GLint j1, GLint j2);
+void ( APIENTRY * qglEvalPoint1 )(GLint i);
+void ( APIENTRY * qglEvalPoint2 )(GLint i, GLint j);
+void ( APIENTRY * qglFeedbackBuffer )(GLsizei size, GLenum type, GLfloat *buffer);
+void ( APIENTRY * qglFinish )(void);
+void ( APIENTRY * qglFlush )(void);
+void ( APIENTRY * qglFogf )(GLenum pname, GLfloat param);
+void ( APIENTRY * qglFogfv )(GLenum pname, const GLfloat *params);
+void ( APIENTRY * qglFogi )(GLenum pname, GLint param);
+void ( APIENTRY * qglFogiv )(GLenum pname, const GLint *params);
+void ( APIENTRY * qglFrontFace )(GLenum mode);
+void ( APIENTRY * qglFrustum )(GLdouble left, GLdouble right, GLdouble bottom, GLdouble top, GLdouble zNear, GLdouble zFar);
+GLuint ( APIENTRY * qglGenLists )(GLsizei range);
+void ( APIENTRY * qglGenTextures )(GLsizei n, GLuint *textures);
+void ( APIENTRY * qglGetBooleanv )(GLenum pname, GLboolean *params);
+void ( APIENTRY * qglGetClipPlane )(GLenum plane, GLdouble *equation);
+void ( APIENTRY * qglGetDoublev )(GLenum pname, GLdouble *params);
+GLenum ( APIENTRY * qglGetError )(void);
+void ( APIENTRY * qglGetFloatv )(GLenum pname, GLfloat *params);
+void ( APIENTRY * qglGetIntegerv )(GLenum pname, GLint *params);
+void ( APIENTRY * qglGetLightfv )(GLenum light, GLenum pname, GLfloat *params);
+void ( APIENTRY * qglGetLightiv )(GLenum light, GLenum pname, GLint *params);
+void ( APIENTRY * qglGetMapdv )(GLenum target, GLenum query, GLdouble *v);
+void ( APIENTRY * qglGetMapfv )(GLenum target, GLenum query, GLfloat *v);
+void ( APIENTRY * qglGetMapiv )(GLenum target, GLenum query, GLint *v);
+void ( APIENTRY * qglGetMaterialfv )(GLenum face, GLenum pname, GLfloat *params);
+void ( APIENTRY * qglGetMaterialiv )(GLenum face, GLenum pname, GLint *params);
+void ( APIENTRY * qglGetPixelMapfv )(GLenum map, GLfloat *values);
+void ( APIENTRY * qglGetPixelMapuiv )(GLenum map, GLuint *values);
+void ( APIENTRY * qglGetPixelMapusv )(GLenum map, GLushort *values);
+void ( APIENTRY * qglGetPointerv )(GLenum pname, GLvoid* *params);
+void ( APIENTRY * qglGetPolygonStipple )(GLubyte *mask);
+const GLubyte * ( APIENTRY * qglGetString )(GLenum name);
+void ( APIENTRY * qglGetTexEnvfv )(GLenum target, GLenum pname, GLfloat *params);
+void ( APIENTRY * qglGetTexEnviv )(GLenum target, GLenum pname, GLint *params);
+void ( APIENTRY * qglGetTexGendv )(GLenum coord, GLenum pname, GLdouble *params);
+void ( APIENTRY * qglGetTexGenfv )(GLenum coord, GLenum pname, GLfloat *params);
+void ( APIENTRY * qglGetTexGeniv )(GLenum coord, GLenum pname, GLint *params);
+void ( APIENTRY * qglGetTexImage )(GLenum target, GLint level, GLenum format, GLenum type, GLvoid *pixels);
+void ( APIENTRY * qglGetTexLevelParameterfv )(GLenum target, GLint level, GLenum pname, GLfloat *params);
+void ( APIENTRY * qglGetTexLevelParameteriv )(GLenum target, GLint level, GLenum pname, GLint *params);
+void ( APIENTRY * qglGetTexParameterfv )(GLenum target, GLenum pname, GLfloat *params);
+void ( APIENTRY * qglGetTexParameteriv )(GLenum target, GLenum pname, GLint *params);
+void ( APIENTRY * qglHint )(GLenum target, GLenum mode);
+void ( APIENTRY * qglIndexMask )(GLuint mask);
+void ( APIENTRY * qglIndexPointer )(GLenum type, GLsizei stride, const GLvoid *pointer);
+void ( APIENTRY * qglIndexd )(GLdouble c);
+void ( APIENTRY * qglIndexdv )(const GLdouble *c);
+void ( APIENTRY * qglIndexf )(GLfloat c);
+void ( APIENTRY * qglIndexfv )(const GLfloat *c);
+void ( APIENTRY * qglIndexi )(GLint c);
+void ( APIENTRY * qglIndexiv )(const GLint *c);
+void ( APIENTRY * qglIndexs )(GLshort c);
+void ( APIENTRY * qglIndexsv )(const GLshort *c);
+void ( APIENTRY * qglIndexub )(GLubyte c);
+void ( APIENTRY * qglIndexubv )(const GLubyte *c);
+void ( APIENTRY * qglInitNames )(void);
+void ( APIENTRY * qglInterleavedArrays )(GLenum format, GLsizei stride, const GLvoid *pointer);
+GLboolean ( APIENTRY * qglIsEnabled )(GLenum cap);
+GLboolean ( APIENTRY * qglIsList )(GLuint list);
+GLboolean ( APIENTRY * qglIsTexture )(GLuint texture);
+void ( APIENTRY * qglLightModelf )(GLenum pname, GLfloat param);
+void ( APIENTRY * qglLightModelfv )(GLenum pname, const GLfloat *params);
+void ( APIENTRY * qglLightModeli )(GLenum pname, GLint param);
+void ( APIENTRY * qglLightModeliv )(GLenum pname, const GLint *params);
+void ( APIENTRY * qglLightf )(GLenum light, GLenum pname, GLfloat param);
+void ( APIENTRY * qglLightfv )(GLenum light, GLenum pname, const GLfloat *params);
+void ( APIENTRY * qglLighti )(GLenum light, GLenum pname, GLint param);
+void ( APIENTRY * qglLightiv )(GLenum light, GLenum pname, const GLint *params);
+void ( APIENTRY * qglLineStipple )(GLint factor, GLushort pattern);
+void ( APIENTRY * qglLineWidth )(GLfloat width);
+void ( APIENTRY * qglListBase )(GLuint base);
+void ( APIENTRY * qglLoadIdentity )(void);
+void ( APIENTRY * qglLoadMatrixd )(const GLdouble *m);
+void ( APIENTRY * qglLoadMatrixf )(const GLfloat *m);
+void ( APIENTRY * qglLoadName )(GLuint name);
+void ( APIENTRY * qglLogicOp )(GLenum opcode);
+void ( APIENTRY * qglMap1d )(GLenum target, GLdouble u1, GLdouble u2, GLint stride, GLint order, const GLdouble *points);
+void ( APIENTRY * qglMap1f )(GLenum target, GLfloat u1, GLfloat u2, GLint stride, GLint order, const GLfloat *points);
+void ( APIENTRY * qglMap2d )(GLenum target, GLdouble u1, GLdouble u2, GLint ustride, GLint uorder, GLdouble v1, GLdouble v2, GLint vstride, GLint vorder, const GLdouble *points);
+void ( APIENTRY * qglMap2f )(GLenum target, GLfloat u1, GLfloat u2, GLint ustride, GLint uorder, GLfloat v1, GLfloat v2, GLint vstride, GLint vorder, const GLfloat *points);
+void ( APIENTRY * qglMapGrid1d )(GLint un, GLdouble u1, GLdouble u2);
+void ( APIENTRY * qglMapGrid1f )(GLint un, GLfloat u1, GLfloat u2);
+void ( APIENTRY * qglMapGrid2d )(GLint un, GLdouble u1, GLdouble u2, GLint vn, GLdouble v1, GLdouble v2);
+void ( APIENTRY * qglMapGrid2f )(GLint un, GLfloat u1, GLfloat u2, GLint vn, GLfloat v1, GLfloat v2);
+void ( APIENTRY * qglMaterialf )(GLenum face, GLenum pname, GLfloat param);
+void ( APIENTRY * qglMaterialfv )(GLenum face, GLenum pname, const GLfloat *params);
+void ( APIENTRY * qglMateriali )(GLenum face, GLenum pname, GLint param);
+void ( APIENTRY * qglMaterialiv )(GLenum face, GLenum pname, const GLint *params);
+void ( APIENTRY * qglMatrixMode )(GLenum mode);
+void ( APIENTRY * qglMultMatrixd )(const GLdouble *m);
+void ( APIENTRY * qglMultMatrixf )(const GLfloat *m);
+void ( APIENTRY * qglNewList )(GLuint list, GLenum mode);
+void ( APIENTRY * qglNormal3b )(GLbyte nx, GLbyte ny, GLbyte nz);
+void ( APIENTRY * qglNormal3bv )(const GLbyte *v);
+void ( APIENTRY * qglNormal3d )(GLdouble nx, GLdouble ny, GLdouble nz);
+void ( APIENTRY * qglNormal3dv )(const GLdouble *v);
+void ( APIENTRY * qglNormal3f )(GLfloat nx, GLfloat ny, GLfloat nz);
+void ( APIENTRY * qglNormal3fv )(const GLfloat *v);
+void ( APIENTRY * qglNormal3i )(GLint nx, GLint ny, GLint nz);
+void ( APIENTRY * qglNormal3iv )(const GLint *v);
+void ( APIENTRY * qglNormal3s )(GLshort nx, GLshort ny, GLshort nz);
+void ( APIENTRY * qglNormal3sv )(const GLshort *v);
+void ( APIENTRY * qglNormalPointer )(GLenum type, GLsizei stride, const GLvoid *pointer);
+void ( APIENTRY * qglOrtho )(GLdouble left, GLdouble right, GLdouble bottom, GLdouble top, GLdouble zNear, GLdouble zFar);
+void ( APIENTRY * qglPassThrough )(GLfloat token);
+void ( APIENTRY * qglPixelMapfv )(GLenum map, GLsizei mapsize, const GLfloat *values);
+void ( APIENTRY * qglPixelMapuiv )(GLenum map, GLsizei mapsize, const GLuint *values);
+void ( APIENTRY * qglPixelMapusv )(GLenum map, GLsizei mapsize, const GLushort *values);
+void ( APIENTRY * qglPixelStoref )(GLenum pname, GLfloat param);
+void ( APIENTRY * qglPixelStorei )(GLenum pname, GLint param);
+void ( APIENTRY * qglPixelTransferf )(GLenum pname, GLfloat param);
+void ( APIENTRY * qglPixelTransferi )(GLenum pname, GLint param);
+void ( APIENTRY * qglPixelZoom )(GLfloat xfactor, GLfloat yfactor);
+void ( APIENTRY * qglPointSize )(GLfloat size);
+void ( APIENTRY * qglPolygonMode )(GLenum face, GLenum mode);
+void ( APIENTRY * qglPolygonOffset )(GLfloat factor, GLfloat units);
+void ( APIENTRY * qglPolygonStipple )(const GLubyte *mask);
+void ( APIENTRY * qglPopAttrib )(void);
+void ( APIENTRY * qglPopClientAttrib )(void);
+void ( APIENTRY * qglPopMatrix )(void);
+void ( APIENTRY * qglPopName )(void);
+void ( APIENTRY * qglPrioritizeTextures )(GLsizei n, const GLuint *textures, const GLclampf *priorities);
+void ( APIENTRY * qglPushAttrib )(GLbitfield mask);
+void ( APIENTRY * qglPushClientAttrib )(GLbitfield mask);
+void ( APIENTRY * qglPushMatrix )(void);
+void ( APIENTRY * qglPushName )(GLuint name);
+void ( APIENTRY * qglRasterPos2d )(GLdouble x, GLdouble y);
+void ( APIENTRY * qglRasterPos2dv )(const GLdouble *v);
+void ( APIENTRY * qglRasterPos2f )(GLfloat x, GLfloat y);
+void ( APIENTRY * qglRasterPos2fv )(const GLfloat *v);
+void ( APIENTRY * qglRasterPos2i )(GLint x, GLint y);
+void ( APIENTRY * qglRasterPos2iv )(const GLint *v);
+void ( APIENTRY * qglRasterPos2s )(GLshort x, GLshort y);
+void ( APIENTRY * qglRasterPos2sv )(const GLshort *v);
+void ( APIENTRY * qglRasterPos3d )(GLdouble x, GLdouble y, GLdouble z);
+void ( APIENTRY * qglRasterPos3dv )(const GLdouble *v);
+void ( APIENTRY * qglRasterPos3f )(GLfloat x, GLfloat y, GLfloat z);
+void ( APIENTRY * qglRasterPos3fv )(const GLfloat *v);
+void ( APIENTRY * qglRasterPos3i )(GLint x, GLint y, GLint z);
+void ( APIENTRY * qglRasterPos3iv )(const GLint *v);
+void ( APIENTRY * qglRasterPos3s )(GLshort x, GLshort y, GLshort z);
+void ( APIENTRY * qglRasterPos3sv )(const GLshort *v);
+void ( APIENTRY * qglRasterPos4d )(GLdouble x, GLdouble y, GLdouble z, GLdouble w);
+void ( APIENTRY * qglRasterPos4dv )(const GLdouble *v);
+void ( APIENTRY * qglRasterPos4f )(GLfloat x, GLfloat y, GLfloat z, GLfloat w);
+void ( APIENTRY * qglRasterPos4fv )(const GLfloat *v);
+void ( APIENTRY * qglRasterPos4i )(GLint x, GLint y, GLint z, GLint w);
+void ( APIENTRY * qglRasterPos4iv )(const GLint *v);
+void ( APIENTRY * qglRasterPos4s )(GLshort x, GLshort y, GLshort z, GLshort w);
+void ( APIENTRY * qglRasterPos4sv )(const GLshort *v);
+void ( APIENTRY * qglReadBuffer )(GLenum mode);
+void ( APIENTRY * qglReadPixels )(GLint x, GLint y, GLsizei width, GLsizei height, GLenum format, GLenum type, GLvoid *pixels);
+void ( APIENTRY * qglRectd )(GLdouble x1, GLdouble y1, GLdouble x2, GLdouble y2);
+void ( APIENTRY * qglRectdv )(const GLdouble *v1, const GLdouble *v2);
+void ( APIENTRY * qglRectf )(GLfloat x1, GLfloat y1, GLfloat x2, GLfloat y2);
+void ( APIENTRY * qglRectfv )(const GLfloat *v1, const GLfloat *v2);
+void ( APIENTRY * qglRecti )(GLint x1, GLint y1, GLint x2, GLint y2);
+void ( APIENTRY * qglRectiv )(const GLint *v1, const GLint *v2);
+void ( APIENTRY * qglRects )(GLshort x1, GLshort y1, GLshort x2, GLshort y2);
+void ( APIENTRY * qglRectsv )(const GLshort *v1, const GLshort *v2);
+GLint ( APIENTRY * qglRenderMode )(GLenum mode);
+void ( APIENTRY * qglRotated )(GLdouble angle, GLdouble x, GLdouble y, GLdouble z);
+void ( APIENTRY * qglRotatef )(GLfloat angle, GLfloat x, GLfloat y, GLfloat z);
+void ( APIENTRY * qglScaled )(GLdouble x, GLdouble y, GLdouble z);
+void ( APIENTRY * qglScalef )(GLfloat x, GLfloat y, GLfloat z);
+void ( APIENTRY * qglScissor )(GLint x, GLint y, GLsizei width, GLsizei height);
+void ( APIENTRY * qglSelectBuffer )(GLsizei size, GLuint *buffer);
+void ( APIENTRY * qglShadeModel )(GLenum mode);
+void ( APIENTRY * qglStencilFunc )(GLenum func, GLint ref, GLuint mask);
+void ( APIENTRY * qglStencilMask )(GLuint mask);
+void ( APIENTRY * qglStencilOp )(GLenum fail, GLenum zfail, GLenum zpass);
+void ( APIENTRY * qglTexCoord1d )(GLdouble s);
+void ( APIENTRY * qglTexCoord1dv )(const GLdouble *v);
+void ( APIENTRY * qglTexCoord1f )(GLfloat s);
+void ( APIENTRY * qglTexCoord1fv )(const GLfloat *v);
+void ( APIENTRY * qglTexCoord1i )(GLint s);
+void ( APIENTRY * qglTexCoord1iv )(const GLint *v);
+void ( APIENTRY * qglTexCoord1s )(GLshort s);
+void ( APIENTRY * qglTexCoord1sv )(const GLshort *v);
+void ( APIENTRY * qglTexCoord2d )(GLdouble s, GLdouble t);
+void ( APIENTRY * qglTexCoord2dv )(const GLdouble *v);
+void ( APIENTRY * qglTexCoord2f )(GLfloat s, GLfloat t);
+void ( APIENTRY * qglTexCoord2fv )(const GLfloat *v);
+void ( APIENTRY * qglTexCoord2i )(GLint s, GLint t);
+void ( APIENTRY * qglTexCoord2iv )(const GLint *v);
+void ( APIENTRY * qglTexCoord2s )(GLshort s, GLshort t);
+void ( APIENTRY * qglTexCoord2sv )(const GLshort *v);
+void ( APIENTRY * qglTexCoord3d )(GLdouble s, GLdouble t, GLdouble r);
+void ( APIENTRY * qglTexCoord3dv )(const GLdouble *v);
+void ( APIENTRY * qglTexCoord3f )(GLfloat s, GLfloat t, GLfloat r);
+void ( APIENTRY * qglTexCoord3fv )(const GLfloat *v);
+void ( APIENTRY * qglTexCoord3i )(GLint s, GLint t, GLint r);
+void ( APIENTRY * qglTexCoord3iv )(const GLint *v);
+void ( APIENTRY * qglTexCoord3s )(GLshort s, GLshort t, GLshort r);
+void ( APIENTRY * qglTexCoord3sv )(const GLshort *v);
+void ( APIENTRY * qglTexCoord4d )(GLdouble s, GLdouble t, GLdouble r, GLdouble q);
+void ( APIENTRY * qglTexCoord4dv )(const GLdouble *v);
+void ( APIENTRY * qglTexCoord4f )(GLfloat s, GLfloat t, GLfloat r, GLfloat q);
+void ( APIENTRY * qglTexCoord4fv )(const GLfloat *v);
+void ( APIENTRY * qglTexCoord4i )(GLint s, GLint t, GLint r, GLint q);
+void ( APIENTRY * qglTexCoord4iv )(const GLint *v);
+void ( APIENTRY * qglTexCoord4s )(GLshort s, GLshort t, GLshort r, GLshort q);
+void ( APIENTRY * qglTexCoord4sv )(const GLshort *v);
+void ( APIENTRY * qglTexCoordPointer )(GLint size, GLenum type, GLsizei stride, const GLvoid *pointer);
+void ( APIENTRY * qglTexEnvf )(GLenum target, GLenum pname, GLfloat param);
+void ( APIENTRY * qglTexEnvfv )(GLenum target, GLenum pname, const GLfloat *params);
+void ( APIENTRY * qglTexEnvi )(GLenum target, GLenum pname, GLint param);
+void ( APIENTRY * qglTexEnviv )(GLenum target, GLenum pname, const GLint *params);
+void ( APIENTRY * qglTexGend )(GLenum coord, GLenum pname, GLdouble param);
+void ( APIENTRY * qglTexGendv )(GLenum coord, GLenum pname, const GLdouble *params);
+void ( APIENTRY * qglTexGenf )(GLenum coord, GLenum pname, GLfloat param);
+void ( APIENTRY * qglTexGenfv )(GLenum coord, GLenum pname, const GLfloat *params);
+void ( APIENTRY * qglTexGeni )(GLenum coord, GLenum pname, GLint param);
+void ( APIENTRY * qglTexGeniv )(GLenum coord, GLenum pname, const GLint *params);
+void ( APIENTRY * qglTexImage1D )(GLenum target, GLint level, GLint internalformat, GLsizei width, GLint border, GLenum format, GLenum type, const GLvoid *pixels);
+void ( APIENTRY * qglTexImage2D )(GLenum target, GLint level, GLint internalformat, GLsizei width, GLsizei height, GLint border, GLenum format, GLenum type, const GLvoid *pixels);
+void ( APIENTRY * qglTexParameterf )(GLenum target, GLenum pname, GLfloat param);
+void ( APIENTRY * qglTexParameterfv )(GLenum target, GLenum pname, const GLfloat *params);
+void ( APIENTRY * qglTexParameteri )(GLenum target, GLenum pname, GLint param);
+void ( APIENTRY * qglTexParameteriv )(GLenum target, GLenum pname, const GLint *params);
+void ( APIENTRY * qglTexSubImage1D )(GLenum target, GLint level, GLint xoffset, GLsizei width, GLenum format, GLenum type, const GLvoid *pixels);
+void ( APIENTRY * qglTexSubImage2D )(GLenum target, GLint level, GLint xoffset, GLint yoffset, GLsizei width, GLsizei height, GLenum format, GLenum type, const GLvoid *pixels);
+void ( APIENTRY * qglTranslated )(GLdouble x, GLdouble y, GLdouble z);
+void ( APIENTRY * qglTranslatef )(GLfloat x, GLfloat y, GLfloat z);
+void ( APIENTRY * qglVertex2d )(GLdouble x, GLdouble y);
+void ( APIENTRY * qglVertex2dv )(const GLdouble *v);
+void ( APIENTRY * qglVertex2f )(GLfloat x, GLfloat y);
+void ( APIENTRY * qglVertex2fv )(const GLfloat *v);
+void ( APIENTRY * qglVertex2i )(GLint x, GLint y);
+void ( APIENTRY * qglVertex2iv )(const GLint *v);
+void ( APIENTRY * qglVertex2s )(GLshort x, GLshort y);
+void ( APIENTRY * qglVertex2sv )(const GLshort *v);
+void ( APIENTRY * qglVertex3d )(GLdouble x, GLdouble y, GLdouble z);
+void ( APIENTRY * qglVertex3dv )(const GLdouble *v);
+void ( APIENTRY * qglVertex3f )(GLfloat x, GLfloat y, GLfloat z);
+void ( APIENTRY * qglVertex3fv )(const GLfloat *v);
+void ( APIENTRY * qglVertex3i )(GLint x, GLint y, GLint z);
+void ( APIENTRY * qglVertex3iv )(const GLint *v);
+void ( APIENTRY * qglVertex3s )(GLshort x, GLshort y, GLshort z);
+void ( APIENTRY * qglVertex3sv )(const GLshort *v);
+void ( APIENTRY * qglVertex4d )(GLdouble x, GLdouble y, GLdouble z, GLdouble w);
+void ( APIENTRY * qglVertex4dv )(const GLdouble *v);
+void ( APIENTRY * qglVertex4f )(GLfloat x, GLfloat y, GLfloat z, GLfloat w);
+void ( APIENTRY * qglVertex4fv )(const GLfloat *v);
+void ( APIENTRY * qglVertex4i )(GLint x, GLint y, GLint z, GLint w);
+void ( APIENTRY * qglVertex4iv )(const GLint *v);
+void ( APIENTRY * qglVertex4s )(GLshort x, GLshort y, GLshort z, GLshort w);
+void ( APIENTRY * qglVertex4sv )(const GLshort *v);
+void ( APIENTRY * qglVertexPointer )(GLint size, GLenum type, GLsizei stride, const GLvoid *pointer);
+void ( APIENTRY * qglViewport )(GLint x, GLint y, GLsizei width, GLsizei height);
+
+void ( APIENTRY * qglMultiTexCoord2fARB )( GLenum texture, GLfloat s, GLfloat t );
+void ( APIENTRY * qglActiveTextureARB )( GLenum texture );
+void ( APIENTRY * qglClientActiveTextureARB )( GLenum texture );
+
+void ( APIENTRY * qglLockArraysEXT)( int, int);
+void ( APIENTRY * qglUnlockArraysEXT) ( void );
+
+void ( APIENTRY * qglPointParameterfEXT)( GLenum param, GLfloat value );
+void ( APIENTRY * qglPointParameterfvEXT)( GLenum param, const GLfloat *value );
+void ( APIENTRY * qglColorTableEXT)( int, int, int, int, int, const void * );
+void ( APIENTRY * qgl3DfxSetPaletteEXT)( GLuint * );
+void ( APIENTRY * qglSelectTextureSGIS)( GLenum );
+void ( APIENTRY * qglMTexCoord2fSGIS)( GLenum, GLfloat, GLfloat );
+
+static void ( APIENTRY * dllAccum )(GLenum op, GLfloat value);
+static void ( APIENTRY * dllAlphaFunc )(GLenum func, GLclampf ref);
+GLboolean ( APIENTRY * dllAreTexturesResident )(GLsizei n, const GLuint *textures, GLboolean *residences);
+static void ( APIENTRY * dllArrayElement )(GLint i);
+static void ( APIENTRY * dllBegin )(GLenum mode);
+static void ( APIENTRY * dllBindTexture )(GLenum target, GLuint texture);
+static void ( APIENTRY * dllBitmap )(GLsizei width, GLsizei height, GLfloat xorig, GLfloat yorig, GLfloat xmove, GLfloat ymove, const GLubyte *bitmap);
+static void ( APIENTRY * dllBlendFunc )(GLenum sfactor, GLenum dfactor);
+static void ( APIENTRY * dllCallList )(GLuint list);
+static void ( APIENTRY * dllCallLists )(GLsizei n, GLenum type, const GLvoid *lists);
+static void ( APIENTRY * dllClear )(GLbitfield mask);
+static void ( APIENTRY * dllClearAccum )(GLfloat red, GLfloat green, GLfloat blue, GLfloat alpha);
+static void ( APIENTRY * dllClearColor )(GLclampf red, GLclampf green, GLclampf blue, GLclampf alpha);
+static void ( APIENTRY * dllClearDepth )(GLclampd depth);
+static void ( APIENTRY * dllClearIndex )(GLfloat c);
+static void ( APIENTRY * dllClearStencil )(GLint s);
+static void ( APIENTRY * dllClipPlane )(GLenum plane, const GLdouble *equation);
+static void ( APIENTRY * dllColor3b )(GLbyte red, GLbyte green, GLbyte blue);
+static void ( APIENTRY * dllColor3bv )(const GLbyte *v);
+static void ( APIENTRY * dllColor3d )(GLdouble red, GLdouble green, GLdouble blue);
+static void ( APIENTRY * dllColor3dv )(const GLdouble *v);
+static void ( APIENTRY * dllColor3f )(GLfloat red, GLfloat green, GLfloat blue);
+static void ( APIENTRY * dllColor3fv )(const GLfloat *v);
+static void ( APIENTRY * dllColor3i )(GLint red, GLint green, GLint blue);
+static void ( APIENTRY * dllColor3iv )(const GLint *v);
+static void ( APIENTRY * dllColor3s )(GLshort red, GLshort green, GLshort blue);
+static void ( APIENTRY * dllColor3sv )(const GLshort *v);
+static void ( APIENTRY * dllColor3ub )(GLubyte red, GLubyte green, GLubyte blue);
+static void ( APIENTRY * dllColor3ubv )(const GLubyte *v);
+static void ( APIENTRY * dllColor3ui )(GLuint red, GLuint green, GLuint blue);
+static void ( APIENTRY * dllColor3uiv )(const GLuint *v);
+static void ( APIENTRY * dllColor3us )(GLushort red, GLushort green, GLushort blue);
+static void ( APIENTRY * dllColor3usv )(const GLushort *v);
+static void ( APIENTRY * dllColor4b )(GLbyte red, GLbyte green, GLbyte blue, GLbyte alpha);
+static void ( APIENTRY * dllColor4bv )(const GLbyte *v);
+static void ( APIENTRY * dllColor4d )(GLdouble red, GLdouble green, GLdouble blue, GLdouble alpha);
+static void ( APIENTRY * dllColor4dv )(const GLdouble *v);
+static void ( APIENTRY * dllColor4f )(GLfloat red, GLfloat green, GLfloat blue, GLfloat alpha);
+static void ( APIENTRY * dllColor4fv )(const GLfloat *v);
+static void ( APIENTRY * dllColor4i )(GLint red, GLint green, GLint blue, GLint alpha);
+static void ( APIENTRY * dllColor4iv )(const GLint *v);
+static void ( APIENTRY * dllColor4s )(GLshort red, GLshort green, GLshort blue, GLshort alpha);
+static void ( APIENTRY * dllColor4sv )(const GLshort *v);
+static void ( APIENTRY * dllColor4ub )(GLubyte red, GLubyte green, GLubyte blue, GLubyte alpha);
+static void ( APIENTRY * dllColor4ubv )(const GLubyte *v);
+static void ( APIENTRY * dllColor4ui )(GLuint red, GLuint green, GLuint blue, GLuint alpha);
+static void ( APIENTRY * dllColor4uiv )(const GLuint *v);
+static void ( APIENTRY * dllColor4us )(GLushort red, GLushort green, GLushort blue, GLushort alpha);
+static void ( APIENTRY * dllColor4usv )(const GLushort *v);
+static void ( APIENTRY * dllColorMask )(GLboolean red, GLboolean green, GLboolean blue, GLboolean alpha);
+static void ( APIENTRY * dllColorMaterial )(GLenum face, GLenum mode);
+static void ( APIENTRY * dllColorPointer )(GLint size, GLenum type, GLsizei stride, const GLvoid *pointer);
+static void ( APIENTRY * dllCopyPixels )(GLint x, GLint y, GLsizei width, GLsizei height, GLenum type);
+static void ( APIENTRY * dllCopyTexImage1D )(GLenum target, GLint level, GLenum internalFormat, GLint x, GLint y, GLsizei width, GLint border);
+static void ( APIENTRY * dllCopyTexImage2D )(GLenum target, GLint level, GLenum internalFormat, GLint x, GLint y, GLsizei width, GLsizei height, GLint border);
+static void ( APIENTRY * dllCopyTexSubImage1D )(GLenum target, GLint level, GLint xoffset, GLint x, GLint y, GLsizei width);
+static void ( APIENTRY * dllCopyTexSubImage2D )(GLenum target, GLint level, GLint xoffset, GLint yoffset, GLint x, GLint y, GLsizei width, GLsizei height);
+static void ( APIENTRY * dllCullFace )(GLenum mode);
+static void ( APIENTRY * dllDeleteLists )(GLuint list, GLsizei range);
+static void ( APIENTRY * dllDeleteTextures )(GLsizei n, const GLuint *textures);
+static void ( APIENTRY * dllDepthFunc )(GLenum func);
+static void ( APIENTRY * dllDepthMask )(GLboolean flag);
+static void ( APIENTRY * dllDepthRange )(GLclampd zNear, GLclampd zFar);
+static void ( APIENTRY * dllDisable )(GLenum cap);
+static void ( APIENTRY * dllDisableClientState )(GLenum array);
+static void ( APIENTRY * dllDrawArrays )(GLenum mode, GLint first, GLsizei count);
+static void ( APIENTRY * dllDrawBuffer )(GLenum mode);
+static void ( APIENTRY * dllDrawElements )(GLenum mode, GLsizei count, GLenum type, const GLvoid *indices);
+static void ( APIENTRY * dllDrawPixels )(GLsizei width, GLsizei height, GLenum format, GLenum type, const GLvoid *pixels);
+static void ( APIENTRY * dllEdgeFlag )(GLboolean flag);
+static void ( APIENTRY * dllEdgeFlagPointer )(GLsizei stride, const GLvoid *pointer);
+static void ( APIENTRY * dllEdgeFlagv )(const GLboolean *flag);
+static void ( APIENTRY * dllEnable )(GLenum cap);
+static void ( APIENTRY * dllEnableClientState )(GLenum array);
+static void ( APIENTRY * dllEnd )(void);
+static void ( APIENTRY * dllEndList )(void);
+static void ( APIENTRY * dllEvalCoord1d )(GLdouble u);
+static void ( APIENTRY * dllEvalCoord1dv )(const GLdouble *u);
+static void ( APIENTRY * dllEvalCoord1f )(GLfloat u);
+static void ( APIENTRY * dllEvalCoord1fv )(const GLfloat *u);
+static void ( APIENTRY * dllEvalCoord2d )(GLdouble u, GLdouble v);
+static void ( APIENTRY * dllEvalCoord2dv )(const GLdouble *u);
+static void ( APIENTRY * dllEvalCoord2f )(GLfloat u, GLfloat v);
+static void ( APIENTRY * dllEvalCoord2fv )(const GLfloat *u);
+static void ( APIENTRY * dllEvalMesh1 )(GLenum mode, GLint i1, GLint i2);
+static void ( APIENTRY * dllEvalMesh2 )(GLenum mode, GLint i1, GLint i2, GLint j1, GLint j2);
+static void ( APIENTRY * dllEvalPoint1 )(GLint i);
+static void ( APIENTRY * dllEvalPoint2 )(GLint i, GLint j);
+static void ( APIENTRY * dllFeedbackBuffer )(GLsizei size, GLenum type, GLfloat *buffer);
+static void ( APIENTRY * dllFinish )(void);
+static void ( APIENTRY * dllFlush )(void);
+static void ( APIENTRY * dllFogf )(GLenum pname, GLfloat param);
+static void ( APIENTRY * dllFogfv )(GLenum pname, const GLfloat *params);
+static void ( APIENTRY * dllFogi )(GLenum pname, GLint param);
+static void ( APIENTRY * dllFogiv )(GLenum pname, const GLint *params);
+static void ( APIENTRY * dllFrontFace )(GLenum mode);
+static void ( APIENTRY * dllFrustum )(GLdouble left, GLdouble right, GLdouble bottom, GLdouble top, GLdouble zNear, GLdouble zFar);
+GLuint ( APIENTRY * dllGenLists )(GLsizei range);
+static void ( APIENTRY * dllGenTextures )(GLsizei n, GLuint *textures);
+static void ( APIENTRY * dllGetBooleanv )(GLenum pname, GLboolean *params);
+static void ( APIENTRY * dllGetClipPlane )(GLenum plane, GLdouble *equation);
+static void ( APIENTRY * dllGetDoublev )(GLenum pname, GLdouble *params);
+GLenum ( APIENTRY * dllGetError )(void);
+static void ( APIENTRY * dllGetFloatv )(GLenum pname, GLfloat *params);
+static void ( APIENTRY * dllGetIntegerv )(GLenum pname, GLint *params);
+static void ( APIENTRY * dllGetLightfv )(GLenum light, GLenum pname, GLfloat *params);
+static void ( APIENTRY * dllGetLightiv )(GLenum light, GLenum pname, GLint *params);
+static void ( APIENTRY * dllGetMapdv )(GLenum target, GLenum query, GLdouble *v);
+static void ( APIENTRY * dllGetMapfv )(GLenum target, GLenum query, GLfloat *v);
+static void ( APIENTRY * dllGetMapiv )(GLenum target, GLenum query, GLint *v);
+static void ( APIENTRY * dllGetMaterialfv )(GLenum face, GLenum pname, GLfloat *params);
+static void ( APIENTRY * dllGetMaterialiv )(GLenum face, GLenum pname, GLint *params);
+static void ( APIENTRY * dllGetPixelMapfv )(GLenum map, GLfloat *values);
+static void ( APIENTRY * dllGetPixelMapuiv )(GLenum map, GLuint *values);
+static void ( APIENTRY * dllGetPixelMapusv )(GLenum map, GLushort *values);
+static void ( APIENTRY * dllGetPointerv )(GLenum pname, GLvoid* *params);
+static void ( APIENTRY * dllGetPolygonStipple )(GLubyte *mask);
+const GLubyte * ( APIENTRY * dllGetString )(GLenum name);
+static void ( APIENTRY * dllGetTexEnvfv )(GLenum target, GLenum pname, GLfloat *params);
+static void ( APIENTRY * dllGetTexEnviv )(GLenum target, GLenum pname, GLint *params);
+static void ( APIENTRY * dllGetTexGendv )(GLenum coord, GLenum pname, GLdouble *params);
+static void ( APIENTRY * dllGetTexGenfv )(GLenum coord, GLenum pname, GLfloat *params);
+static void ( APIENTRY * dllGetTexGeniv )(GLenum coord, GLenum pname, GLint *params);
+static void ( APIENTRY * dllGetTexImage )(GLenum target, GLint level, GLenum format, GLenum type, GLvoid *pixels);
+static void ( APIENTRY * dllGetTexLevelParameterfv )(GLenum target, GLint level, GLenum pname, GLfloat *params);
+static void ( APIENTRY * dllGetTexLevelParameteriv )(GLenum target, GLint level, GLenum pname, GLint *params);
+static void ( APIENTRY * dllGetTexParameterfv )(GLenum target, GLenum pname, GLfloat *params);
+static void ( APIENTRY * dllGetTexParameteriv )(GLenum target, GLenum pname, GLint *params);
+static void ( APIENTRY * dllHint )(GLenum target, GLenum mode);
+static void ( APIENTRY * dllIndexMask )(GLuint mask);
+static void ( APIENTRY * dllIndexPointer )(GLenum type, GLsizei stride, const GLvoid *pointer);
+static void ( APIENTRY * dllIndexd )(GLdouble c);
+static void ( APIENTRY * dllIndexdv )(const GLdouble *c);
+static void ( APIENTRY * dllIndexf )(GLfloat c);
+static void ( APIENTRY * dllIndexfv )(const GLfloat *c);
+static void ( APIENTRY * dllIndexi )(GLint c);
+static void ( APIENTRY * dllIndexiv )(const GLint *c);
+static void ( APIENTRY * dllIndexs )(GLshort c);
+static void ( APIENTRY * dllIndexsv )(const GLshort *c);
+static void ( APIENTRY * dllIndexub )(GLubyte c);
+static void ( APIENTRY * dllIndexubv )(const GLubyte *c);
+static void ( APIENTRY * dllInitNames )(void);
+static void ( APIENTRY * dllInterleavedArrays )(GLenum format, GLsizei stride, const GLvoid *pointer);
+GLboolean ( APIENTRY * dllIsEnabled )(GLenum cap);
+GLboolean ( APIENTRY * dllIsList )(GLuint list);
+GLboolean ( APIENTRY * dllIsTexture )(GLuint texture);
+static void ( APIENTRY * dllLightModelf )(GLenum pname, GLfloat param);
+static void ( APIENTRY * dllLightModelfv )(GLenum pname, const GLfloat *params);
+static void ( APIENTRY * dllLightModeli )(GLenum pname, GLint param);
+static void ( APIENTRY * dllLightModeliv )(GLenum pname, const GLint *params);
+static void ( APIENTRY * dllLightf )(GLenum light, GLenum pname, GLfloat param);
+static void ( APIENTRY * dllLightfv )(GLenum light, GLenum pname, const GLfloat *params);
+static void ( APIENTRY * dllLighti )(GLenum light, GLenum pname, GLint param);
+static void ( APIENTRY * dllLightiv )(GLenum light, GLenum pname, const GLint *params);
+static void ( APIENTRY * dllLineStipple )(GLint factor, GLushort pattern);
+static void ( APIENTRY * dllLineWidth )(GLfloat width);
+static void ( APIENTRY * dllListBase )(GLuint base);
+static void ( APIENTRY * dllLoadIdentity )(void);
+static void ( APIENTRY * dllLoadMatrixd )(const GLdouble *m);
+static void ( APIENTRY * dllLoadMatrixf )(const GLfloat *m);
+static void ( APIENTRY * dllLoadName )(GLuint name);
+static void ( APIENTRY * dllLogicOp )(GLenum opcode);
+static void ( APIENTRY * dllMap1d )(GLenum target, GLdouble u1, GLdouble u2, GLint stride, GLint order, const GLdouble *points);
+static void ( APIENTRY * dllMap1f )(GLenum target, GLfloat u1, GLfloat u2, GLint stride, GLint order, const GLfloat *points);
+static void ( APIENTRY * dllMap2d )(GLenum target, GLdouble u1, GLdouble u2, GLint ustride, GLint uorder, GLdouble v1, GLdouble v2, GLint vstride, GLint vorder, const GLdouble *points);
+static void ( APIENTRY * dllMap2f )(GLenum target, GLfloat u1, GLfloat u2, GLint ustride, GLint uorder, GLfloat v1, GLfloat v2, GLint vstride, GLint vorder, const GLfloat *points);
+static void ( APIENTRY * dllMapGrid1d )(GLint un, GLdouble u1, GLdouble u2);
+static void ( APIENTRY * dllMapGrid1f )(GLint un, GLfloat u1, GLfloat u2);
+static void ( APIENTRY * dllMapGrid2d )(GLint un, GLdouble u1, GLdouble u2, GLint vn, GLdouble v1, GLdouble v2);
+static void ( APIENTRY * dllMapGrid2f )(GLint un, GLfloat u1, GLfloat u2, GLint vn, GLfloat v1, GLfloat v2);
+static void ( APIENTRY * dllMaterialf )(GLenum face, GLenum pname, GLfloat param);
+static void ( APIENTRY * dllMaterialfv )(GLenum face, GLenum pname, const GLfloat *params);
+static void ( APIENTRY * dllMateriali )(GLenum face, GLenum pname, GLint param);
+static void ( APIENTRY * dllMaterialiv )(GLenum face, GLenum pname, const GLint *params);
+static void ( APIENTRY * dllMatrixMode )(GLenum mode);
+static void ( APIENTRY * dllMultMatrixd )(const GLdouble *m);
+static void ( APIENTRY * dllMultMatrixf )(const GLfloat *m);
+static void ( APIENTRY * dllNewList )(GLuint list, GLenum mode);
+static void ( APIENTRY * dllNormal3b )(GLbyte nx, GLbyte ny, GLbyte nz);
+static void ( APIENTRY * dllNormal3bv )(const GLbyte *v);
+static void ( APIENTRY * dllNormal3d )(GLdouble nx, GLdouble ny, GLdouble nz);
+static void ( APIENTRY * dllNormal3dv )(const GLdouble *v);
+static void ( APIENTRY * dllNormal3f )(GLfloat nx, GLfloat ny, GLfloat nz);
+static void ( APIENTRY * dllNormal3fv )(const GLfloat *v);
+static void ( APIENTRY * dllNormal3i )(GLint nx, GLint ny, GLint nz);
+static void ( APIENTRY * dllNormal3iv )(const GLint *v);
+static void ( APIENTRY * dllNormal3s )(GLshort nx, GLshort ny, GLshort nz);
+static void ( APIENTRY * dllNormal3sv )(const GLshort *v);
+static void ( APIENTRY * dllNormalPointer )(GLenum type, GLsizei stride, const GLvoid *pointer);
+static void ( APIENTRY * dllOrtho )(GLdouble left, GLdouble right, GLdouble bottom, GLdouble top, GLdouble zNear, GLdouble zFar);
+static void ( APIENTRY * dllPassThrough )(GLfloat token);
+static void ( APIENTRY * dllPixelMapfv )(GLenum map, GLsizei mapsize, const GLfloat *values);
+static void ( APIENTRY * dllPixelMapuiv )(GLenum map, GLsizei mapsize, const GLuint *values);
+static void ( APIENTRY * dllPixelMapusv )(GLenum map, GLsizei mapsize, const GLushort *values);
+static void ( APIENTRY * dllPixelStoref )(GLenum pname, GLfloat param);
+static void ( APIENTRY * dllPixelStorei )(GLenum pname, GLint param);
+static void ( APIENTRY * dllPixelTransferf )(GLenum pname, GLfloat param);
+static void ( APIENTRY * dllPixelTransferi )(GLenum pname, GLint param);
+static void ( APIENTRY * dllPixelZoom )(GLfloat xfactor, GLfloat yfactor);
+static void ( APIENTRY * dllPointSize )(GLfloat size);
+static void ( APIENTRY * dllPolygonMode )(GLenum face, GLenum mode);
+static void ( APIENTRY * dllPolygonOffset )(GLfloat factor, GLfloat units);
+static void ( APIENTRY * dllPolygonStipple )(const GLubyte *mask);
+static void ( APIENTRY * dllPopAttrib )(void);
+static void ( APIENTRY * dllPopClientAttrib )(void);
+static void ( APIENTRY * dllPopMatrix )(void);
+static void ( APIENTRY * dllPopName )(void);
+static void ( APIENTRY * dllPrioritizeTextures )(GLsizei n, const GLuint *textures, const GLclampf *priorities);
+static void ( APIENTRY * dllPushAttrib )(GLbitfield mask);
+static void ( APIENTRY * dllPushClientAttrib )(GLbitfield mask);
+static void ( APIENTRY * dllPushMatrix )(void);
+static void ( APIENTRY * dllPushName )(GLuint name);
+static void ( APIENTRY * dllRasterPos2d )(GLdouble x, GLdouble y);
+static void ( APIENTRY * dllRasterPos2dv )(const GLdouble *v);
+static void ( APIENTRY * dllRasterPos2f )(GLfloat x, GLfloat y);
+static void ( APIENTRY * dllRasterPos2fv )(const GLfloat *v);
+static void ( APIENTRY * dllRasterPos2i )(GLint x, GLint y);
+static void ( APIENTRY * dllRasterPos2iv )(const GLint *v);
+static void ( APIENTRY * dllRasterPos2s )(GLshort x, GLshort y);
+static void ( APIENTRY * dllRasterPos2sv )(const GLshort *v);
+static void ( APIENTRY * dllRasterPos3d )(GLdouble x, GLdouble y, GLdouble z);
+static void ( APIENTRY * dllRasterPos3dv )(const GLdouble *v);
+static void ( APIENTRY * dllRasterPos3f )(GLfloat x, GLfloat y, GLfloat z);
+static void ( APIENTRY * dllRasterPos3fv )(const GLfloat *v);
+static void ( APIENTRY * dllRasterPos3i )(GLint x, GLint y, GLint z);
+static void ( APIENTRY * dllRasterPos3iv )(const GLint *v);
+static void ( APIENTRY * dllRasterPos3s )(GLshort x, GLshort y, GLshort z);
+static void ( APIENTRY * dllRasterPos3sv )(const GLshort *v);
+static void ( APIENTRY * dllRasterPos4d )(GLdouble x, GLdouble y, GLdouble z, GLdouble w);
+static void ( APIENTRY * dllRasterPos4dv )(const GLdouble *v);
+static void ( APIENTRY * dllRasterPos4f )(GLfloat x, GLfloat y, GLfloat z, GLfloat w);
+static void ( APIENTRY * dllRasterPos4fv )(const GLfloat *v);
+static void ( APIENTRY * dllRasterPos4i )(GLint x, GLint y, GLint z, GLint w);
+static void ( APIENTRY * dllRasterPos4iv )(const GLint *v);
+static void ( APIENTRY * dllRasterPos4s )(GLshort x, GLshort y, GLshort z, GLshort w);
+static void ( APIENTRY * dllRasterPos4sv )(const GLshort *v);
+static void ( APIENTRY * dllReadBuffer )(GLenum mode);
+static void ( APIENTRY * dllReadPixels )(GLint x, GLint y, GLsizei width, GLsizei height, GLenum format, GLenum type, GLvoid *pixels);
+static void ( APIENTRY * dllRectd )(GLdouble x1, GLdouble y1, GLdouble x2, GLdouble y2);
+static void ( APIENTRY * dllRectdv )(const GLdouble *v1, const GLdouble *v2);
+static void ( APIENTRY * dllRectf )(GLfloat x1, GLfloat y1, GLfloat x2, GLfloat y2);
+static void ( APIENTRY * dllRectfv )(const GLfloat *v1, const GLfloat *v2);
+static void ( APIENTRY * dllRecti )(GLint x1, GLint y1, GLint x2, GLint y2);
+static void ( APIENTRY * dllRectiv )(const GLint *v1, const GLint *v2);
+static void ( APIENTRY * dllRects )(GLshort x1, GLshort y1, GLshort x2, GLshort y2);
+static void ( APIENTRY * dllRectsv )(const GLshort *v1, const GLshort *v2);
+GLint ( APIENTRY * dllRenderMode )(GLenum mode);
+static void ( APIENTRY * dllRotated )(GLdouble angle, GLdouble x, GLdouble y, GLdouble z);
+static void ( APIENTRY * dllRotatef )(GLfloat angle, GLfloat x, GLfloat y, GLfloat z);
+static void ( APIENTRY * dllScaled )(GLdouble x, GLdouble y, GLdouble z);
+static void ( APIENTRY * dllScalef )(GLfloat x, GLfloat y, GLfloat z);
+static void ( APIENTRY * dllScissor )(GLint x, GLint y, GLsizei width, GLsizei height);
+static void ( APIENTRY * dllSelectBuffer )(GLsizei size, GLuint *buffer);
+static void ( APIENTRY * dllShadeModel )(GLenum mode);
+static void ( APIENTRY * dllStencilFunc )(GLenum func, GLint ref, GLuint mask);
+static void ( APIENTRY * dllStencilMask )(GLuint mask);
+static void ( APIENTRY * dllStencilOp )(GLenum fail, GLenum zfail, GLenum zpass);
+static void ( APIENTRY * dllTexCoord1d )(GLdouble s);
+static void ( APIENTRY * dllTexCoord1dv )(const GLdouble *v);
+static void ( APIENTRY * dllTexCoord1f )(GLfloat s);
+static void ( APIENTRY * dllTexCoord1fv )(const GLfloat *v);
+static void ( APIENTRY * dllTexCoord1i )(GLint s);
+static void ( APIENTRY * dllTexCoord1iv )(const GLint *v);
+static void ( APIENTRY * dllTexCoord1s )(GLshort s);
+static void ( APIENTRY * dllTexCoord1sv )(const GLshort *v);
+static void ( APIENTRY * dllTexCoord2d )(GLdouble s, GLdouble t);
+static void ( APIENTRY * dllTexCoord2dv )(const GLdouble *v);
+static void ( APIENTRY * dllTexCoord2f )(GLfloat s, GLfloat t);
+static void ( APIENTRY * dllTexCoord2fv )(const GLfloat *v);
+static void ( APIENTRY * dllTexCoord2i )(GLint s, GLint t);
+static void ( APIENTRY * dllTexCoord2iv )(const GLint *v);
+static void ( APIENTRY * dllTexCoord2s )(GLshort s, GLshort t);
+static void ( APIENTRY * dllTexCoord2sv )(const GLshort *v);
+static void ( APIENTRY * dllTexCoord3d )(GLdouble s, GLdouble t, GLdouble r);
+static void ( APIENTRY * dllTexCoord3dv )(const GLdouble *v);
+static void ( APIENTRY * dllTexCoord3f )(GLfloat s, GLfloat t, GLfloat r);
+static void ( APIENTRY * dllTexCoord3fv )(const GLfloat *v);
+static void ( APIENTRY * dllTexCoord3i )(GLint s, GLint t, GLint r);
+static void ( APIENTRY * dllTexCoord3iv )(const GLint *v);
+static void ( APIENTRY * dllTexCoord3s )(GLshort s, GLshort t, GLshort r);
+static void ( APIENTRY * dllTexCoord3sv )(const GLshort *v);
+static void ( APIENTRY * dllTexCoord4d )(GLdouble s, GLdouble t, GLdouble r, GLdouble q);
+static void ( APIENTRY * dllTexCoord4dv )(const GLdouble *v);
+static void ( APIENTRY * dllTexCoord4f )(GLfloat s, GLfloat t, GLfloat r, GLfloat q);
+static void ( APIENTRY * dllTexCoord4fv )(const GLfloat *v);
+static void ( APIENTRY * dllTexCoord4i )(GLint s, GLint t, GLint r, GLint q);
+static void ( APIENTRY * dllTexCoord4iv )(const GLint *v);
+static void ( APIENTRY * dllTexCoord4s )(GLshort s, GLshort t, GLshort r, GLshort q);
+static void ( APIENTRY * dllTexCoord4sv )(const GLshort *v);
+static void ( APIENTRY * dllTexCoordPointer )(GLint size, GLenum type, GLsizei stride, const GLvoid *pointer);
+static void ( APIENTRY * dllTexEnvf )(GLenum target, GLenum pname, GLfloat param);
+static void ( APIENTRY * dllTexEnvfv )(GLenum target, GLenum pname, const GLfloat *params);
+static void ( APIENTRY * dllTexEnvi )(GLenum target, GLenum pname, GLint param);
+static void ( APIENTRY * dllTexEnviv )(GLenum target, GLenum pname, const GLint *params);
+static void ( APIENTRY * dllTexGend )(GLenum coord, GLenum pname, GLdouble param);
+static void ( APIENTRY * dllTexGendv )(GLenum coord, GLenum pname, const GLdouble *params);
+static void ( APIENTRY * dllTexGenf )(GLenum coord, GLenum pname, GLfloat param);
+static void ( APIENTRY * dllTexGenfv )(GLenum coord, GLenum pname, const GLfloat *params);
+static void ( APIENTRY * dllTexGeni )(GLenum coord, GLenum pname, GLint param);
+static void ( APIENTRY * dllTexGeniv )(GLenum coord, GLenum pname, const GLint *params);
+static void ( APIENTRY * dllTexImage1D )(GLenum target, GLint level, GLint internalformat, GLsizei width, GLint border, GLenum format, GLenum type, const GLvoid *pixels);
+static void ( APIENTRY * dllTexImage2D )(GLenum target, GLint level, GLint internalformat, GLsizei width, GLsizei height, GLint border, GLenum format, GLenum type, const GLvoid *pixels);
+static void ( APIENTRY * dllTexParameterf )(GLenum target, GLenum pname, GLfloat param);
+static void ( APIENTRY * dllTexParameterfv )(GLenum target, GLenum pname, const GLfloat *params);
+static void ( APIENTRY * dllTexParameteri )(GLenum target, GLenum pname, GLint param);
+static void ( APIENTRY * dllTexParameteriv )(GLenum target, GLenum pname, const GLint *params);
+static void ( APIENTRY * dllTexSubImage1D )(GLenum target, GLint level, GLint xoffset, GLsizei width, GLenum format, GLenum type, const GLvoid *pixels);
+static void ( APIENTRY * dllTexSubImage2D )(GLenum target, GLint level, GLint xoffset, GLint yoffset, GLsizei width, GLsizei height, GLenum format, GLenum type, const GLvoid *pixels);
+static void ( APIENTRY * dllTranslated )(GLdouble x, GLdouble y, GLdouble z);
+static void ( APIENTRY * dllTranslatef )(GLfloat x, GLfloat y, GLfloat z);
+static void ( APIENTRY * dllVertex2d )(GLdouble x, GLdouble y);
+static void ( APIENTRY * dllVertex2dv )(const GLdouble *v);
+static void ( APIENTRY * dllVertex2f )(GLfloat x, GLfloat y);
+static void ( APIENTRY * dllVertex2fv )(const GLfloat *v);
+static void ( APIENTRY * dllVertex2i )(GLint x, GLint y);
+static void ( APIENTRY * dllVertex2iv )(const GLint *v);
+static void ( APIENTRY * dllVertex2s )(GLshort x, GLshort y);
+static void ( APIENTRY * dllVertex2sv )(const GLshort *v);
+static void ( APIENTRY * dllVertex3d )(GLdouble x, GLdouble y, GLdouble z);
+static void ( APIENTRY * dllVertex3dv )(const GLdouble *v);
+static void ( APIENTRY * dllVertex3f )(GLfloat x, GLfloat y, GLfloat z);
+static void ( APIENTRY * dllVertex3fv )(const GLfloat *v);
+static void ( APIENTRY * dllVertex3i )(GLint x, GLint y, GLint z);
+static void ( APIENTRY * dllVertex3iv )(const GLint *v);
+static void ( APIENTRY * dllVertex3s )(GLshort x, GLshort y, GLshort z);
+static void ( APIENTRY * dllVertex3sv )(const GLshort *v);
+static void ( APIENTRY * dllVertex4d )(GLdouble x, GLdouble y, GLdouble z, GLdouble w);
+static void ( APIENTRY * dllVertex4dv )(const GLdouble *v);
+static void ( APIENTRY * dllVertex4f )(GLfloat x, GLfloat y, GLfloat z, GLfloat w);
+static void ( APIENTRY * dllVertex4fv )(const GLfloat *v);
+static void ( APIENTRY * dllVertex4i )(GLint x, GLint y, GLint z, GLint w);
+static void ( APIENTRY * dllVertex4iv )(const GLint *v);
+static void ( APIENTRY * dllVertex4s )(GLshort x, GLshort y, GLshort z, GLshort w);
+static void ( APIENTRY * dllVertex4sv )(const GLshort *v);
+static void ( APIENTRY * dllVertexPointer )(GLint size, GLenum type, GLsizei stride, const GLvoid *pointer);
+static void ( APIENTRY * dllViewport )(GLint x, GLint y, GLsizei width, GLsizei height);
+
+static void APIENTRY logAccum(GLenum op, GLfloat value)
+{
+ fprintf( glw_state.log_fp, "glAccum\n" );
+ dllAccum( op, value );
+}
+
+static void APIENTRY logAlphaFunc(GLenum func, GLclampf ref)
+{
+ fprintf( glw_state.log_fp, "glAlphaFunc( 0x%x, %f )\n", func, ref );
+ dllAlphaFunc( func, ref );
+}
+
+static GLboolean APIENTRY logAreTexturesResident(GLsizei n, const GLuint *textures, GLboolean *residences)
+{
+ fprintf( glw_state.log_fp, "glAreTexturesResident\n" );
+ return dllAreTexturesResident( n, textures, residences );
+}
+
+static void APIENTRY logArrayElement(GLint i)
+{
+ fprintf( glw_state.log_fp, "glArrayElement\n" );
+ dllArrayElement( i );
+}
+
+static void APIENTRY logBegin(GLenum mode)
+{
+ fprintf( glw_state.log_fp, "glBegin( 0x%x )\n", mode );
+ dllBegin( mode );
+}
+
+static void APIENTRY logBindTexture(GLenum target, GLuint texture)
+{
+ fprintf( glw_state.log_fp, "glBindTexture( 0x%x, %u )\n", target, texture );
+ dllBindTexture( target, texture );
+}
+
+static void APIENTRY logBitmap(GLsizei width, GLsizei height, GLfloat xorig, GLfloat yorig, GLfloat xmove, GLfloat ymove, const GLubyte *bitmap)
+{
+ fprintf( glw_state.log_fp, "glBitmap\n" );
+ dllBitmap( width, height, xorig, yorig, xmove, ymove, bitmap );
+}
+
+static void APIENTRY logBlendFunc(GLenum sfactor, GLenum dfactor)
+{
+ fprintf( glw_state.log_fp, "glBlendFunc( 0x%x, 0x%x )\n", sfactor, dfactor );
+ dllBlendFunc( sfactor, dfactor );
+}
+
+static void APIENTRY logCallList(GLuint list)
+{
+ fprintf( glw_state.log_fp, "glCallList( %u )\n", list );
+ dllCallList( list );
+}
+
+static void APIENTRY logCallLists(GLsizei n, GLenum type, const void *lists)
+{
+ fprintf( glw_state.log_fp, "glCallLists\n" );
+ dllCallLists( n, type, lists );
+}
+
+static void APIENTRY logClear(GLbitfield mask)
+{
+ fprintf( glw_state.log_fp, "glClear\n" );
+ dllClear( mask );
+}
+
+static void APIENTRY logClearAccum(GLfloat red, GLfloat green, GLfloat blue, GLfloat alpha)
+{
+ fprintf( glw_state.log_fp, "glClearAccum\n" );
+ dllClearAccum( red, green, blue, alpha );
+}
+
+static void APIENTRY logClearColor(GLclampf red, GLclampf green, GLclampf blue, GLclampf alpha)
+{
+ fprintf( glw_state.log_fp, "glClearColor\n" );
+ dllClearColor( red, green, blue, alpha );
+}
+
+static void APIENTRY logClearDepth(GLclampd depth)
+{
+ fprintf( glw_state.log_fp, "glClearDepth\n" );
+ dllClearDepth( depth );
+}
+
+static void APIENTRY logClearIndex(GLfloat c)
+{
+ fprintf( glw_state.log_fp, "glClearIndex\n" );
+ dllClearIndex( c );
+}
+
+static void APIENTRY logClearStencil(GLint s)
+{
+ fprintf( glw_state.log_fp, "glClearStencil\n" );
+ dllClearStencil( s );
+}
+
+static void APIENTRY logClipPlane(GLenum plane, const GLdouble *equation)
+{
+ fprintf( glw_state.log_fp, "glClipPlane\n" );
+ dllClipPlane( plane, equation );
+}
+
+static void APIENTRY logColor3b(GLbyte red, GLbyte green, GLbyte blue)
+{
+ fprintf( glw_state.log_fp, "glColor3b\n" );
+ dllColor3b( red, green, blue );
+}
+
+static void APIENTRY logColor3bv(const GLbyte *v)
+{
+ fprintf( glw_state.log_fp, "glColor3bv\n" );
+ dllColor3bv( v );
+}
+
+static void APIENTRY logColor3d(GLdouble red, GLdouble green, GLdouble blue)
+{
+ fprintf( glw_state.log_fp, "glColor3d\n" );
+ dllColor3d( red, green, blue );
+}
+
+static void APIENTRY logColor3dv(const GLdouble *v)
+{
+ fprintf( glw_state.log_fp, "glColor3dv\n" );
+ dllColor3dv( v );
+}
+
+static void APIENTRY logColor3f(GLfloat red, GLfloat green, GLfloat blue)
+{
+ fprintf( glw_state.log_fp, "glColor3f\n" );
+ dllColor3f( red, green, blue );
+}
+
+static void APIENTRY logColor3fv(const GLfloat *v)
+{
+ fprintf( glw_state.log_fp, "glColor3fv\n" );
+ dllColor3fv( v );
+}
+
+static void APIENTRY logColor3i(GLint red, GLint green, GLint blue)
+{
+ fprintf( glw_state.log_fp, "glColor3i\n" );
+ dllColor3i( red, green, blue );
+}
+
+static void APIENTRY logColor3iv(const GLint *v)
+{
+ fprintf( glw_state.log_fp, "glColor3iv\n" );
+ dllColor3iv( v );
+}
+
+static void APIENTRY logColor3s(GLshort red, GLshort green, GLshort blue)
+{
+ fprintf( glw_state.log_fp, "glColor3s\n" );
+ dllColor3s( red, green, blue );
+}
+
+static void APIENTRY logColor3sv(const GLshort *v)
+{
+ fprintf( glw_state.log_fp, "glColor3sv\n" );
+ dllColor3sv( v );
+}
+
+static void APIENTRY logColor3ub(GLubyte red, GLubyte green, GLubyte blue)
+{
+ fprintf( glw_state.log_fp, "glColor3ub\n" );
+ dllColor3ub( red, green, blue );
+}
+
+static void APIENTRY logColor3ubv(const GLubyte *v)
+{
+ fprintf( glw_state.log_fp, "glColor3ubv\n" );
+ dllColor3ubv( v );
+}
+
+#define SIG( x ) fprintf( glw_state.log_fp, x "\n" )
+
+static void APIENTRY logColor3ui(GLuint red, GLuint green, GLuint blue)
+{
+ SIG( "glColor3ui" );
+ dllColor3ui( red, green, blue );
+}
+
+static void APIENTRY logColor3uiv(const GLuint *v)
+{
+ SIG( "glColor3uiv" );
+ dllColor3uiv( v );
+}
+
+static void APIENTRY logColor3us(GLushort red, GLushort green, GLushort blue)
+{
+ SIG( "glColor3us" );
+ dllColor3us( red, green, blue );
+}
+
+static void APIENTRY logColor3usv(const GLushort *v)
+{
+ SIG( "glColor3usv" );
+ dllColor3usv( v );
+}
+
+static void APIENTRY logColor4b(GLbyte red, GLbyte green, GLbyte blue, GLbyte alpha)
+{
+ SIG( "glColor4b" );
+ dllColor4b( red, green, blue, alpha );
+}
+
+static void APIENTRY logColor4bv(const GLbyte *v)
+{
+ SIG( "glColor4bv" );
+ dllColor4bv( v );
+}
+
+static void APIENTRY logColor4d(GLdouble red, GLdouble green, GLdouble blue, GLdouble alpha)
+{
+ SIG( "glColor4d" );
+ dllColor4d( red, green, blue, alpha );
+}
+static void APIENTRY logColor4dv(const GLdouble *v)
+{
+ SIG( "glColor4dv" );
+ dllColor4dv( v );
+}
+static void APIENTRY logColor4f(GLfloat red, GLfloat green, GLfloat blue, GLfloat alpha)
+{
+ fprintf( glw_state.log_fp, "glColor4f( %f,%f,%f,%f )\n", red, green, blue, alpha );
+ dllColor4f( red, green, blue, alpha );
+}
+static void APIENTRY logColor4fv(const GLfloat *v)
+{
+ fprintf( glw_state.log_fp, "glColor4fv( %f,%f,%f,%f )\n", v[0], v[1], v[2], v[3] );
+ dllColor4fv( v );
+}
+static void APIENTRY logColor4i(GLint red, GLint green, GLint blue, GLint alpha)
+{
+ SIG( "glColor4i" );
+ dllColor4i( red, green, blue, alpha );
+}
+static void APIENTRY logColor4iv(const GLint *v)
+{
+ SIG( "glColor4iv" );
+ dllColor4iv( v );
+}
+static void APIENTRY logColor4s(GLshort red, GLshort green, GLshort blue, GLshort alpha)
+{
+ SIG( "glColor4s" );
+ dllColor4s( red, green, blue, alpha );
+}
+static void APIENTRY logColor4sv(const GLshort *v)
+{
+ SIG( "glColor4sv" );
+ dllColor4sv( v );
+}
+static void APIENTRY logColor4ub(GLubyte red, GLubyte green, GLubyte blue, GLubyte alpha)
+{
+ SIG( "glColor4b" );
+ dllColor4b( red, green, blue, alpha );
+}
+static void APIENTRY logColor4ubv(const GLubyte *v)
+{
+ SIG( "glColor4ubv" );
+ dllColor4ubv( v );
+}
+static void APIENTRY logColor4ui(GLuint red, GLuint green, GLuint blue, GLuint alpha)
+{
+ SIG( "glColor4ui" );
+ dllColor4ui( red, green, blue, alpha );
+}
+static void APIENTRY logColor4uiv(const GLuint *v)
+{
+ SIG( "glColor4uiv" );
+ dllColor4uiv( v );
+}
+static void APIENTRY logColor4us(GLushort red, GLushort green, GLushort blue, GLushort alpha)
+{
+ SIG( "glColor4us" );
+ dllColor4us( red, green, blue, alpha );
+}
+static void APIENTRY logColor4usv(const GLushort *v)
+{
+ SIG( "glColor4usv" );
+ dllColor4usv( v );
+}
+static void APIENTRY logColorMask(GLboolean red, GLboolean green, GLboolean blue, GLboolean alpha)
+{
+ SIG( "glColorMask" );
+ dllColorMask( red, green, blue, alpha );
+}
+static void APIENTRY logColorMaterial(GLenum face, GLenum mode)
+{
+ SIG( "glColorMaterial" );
+ dllColorMaterial( face, mode );
+}
+
+static void APIENTRY logColorPointer(GLint size, GLenum type, GLsizei stride, const void *pointer)
+{
+ SIG( "glColorPointer" );
+ dllColorPointer( size, type, stride, pointer );
+}
+
+static void APIENTRY logCopyPixels(GLint x, GLint y, GLsizei width, GLsizei height, GLenum type)
+{
+ SIG( "glCopyPixels" );
+ dllCopyPixels( x, y, width, height, type );
+}
+
+static void APIENTRY logCopyTexImage1D(GLenum target, GLint level, GLenum internalFormat, GLint x, GLint y, GLsizei width, GLint border)
+{
+ SIG( "glCopyTexImage1D" );
+ dllCopyTexImage1D( target, level, internalFormat, x, y, width, border );
+}
+
+static void APIENTRY logCopyTexImage2D(GLenum target, GLint level, GLenum internalFormat, GLint x, GLint y, GLsizei width, GLsizei height, GLint border)
+{
+ SIG( "glCopyTexImage2D" );
+ dllCopyTexImage2D( target, level, internalFormat, x, y, width, height, border );
+}
+
+static void APIENTRY logCopyTexSubImage1D(GLenum target, GLint level, GLint xoffset, GLint x, GLint y, GLsizei width)
+{
+ SIG( "glCopyTexSubImage1D" );
+ dllCopyTexSubImage1D( target, level, xoffset, x, y, width );
+}
+
+static void APIENTRY logCopyTexSubImage2D(GLenum target, GLint level, GLint xoffset, GLint yoffset, GLint x, GLint y, GLsizei width, GLsizei height)
+{
+ SIG( "glCopyTexSubImage2D" );
+ dllCopyTexSubImage2D( target, level, xoffset, yoffset, x, y, width, height );
+}
+
+static void APIENTRY logCullFace(GLenum mode)
+{
+ SIG( "glCullFace" );
+ dllCullFace( mode );
+}
+
+static void APIENTRY logDeleteLists(GLuint list, GLsizei range)
+{
+ SIG( "glDeleteLists" );
+ dllDeleteLists( list, range );
+}
+
+static void APIENTRY logDeleteTextures(GLsizei n, const GLuint *textures)
+{
+ SIG( "glDeleteTextures" );
+ dllDeleteTextures( n, textures );
+}
+
+static void APIENTRY logDepthFunc(GLenum func)
+{
+ SIG( "glDepthFunc" );
+ dllDepthFunc( func );
+}
+
+static void APIENTRY logDepthMask(GLboolean flag)
+{
+ SIG( "glDepthMask" );
+ dllDepthMask( flag );
+}
+
+static void APIENTRY logDepthRange(GLclampd zNear, GLclampd zFar)
+{
+ SIG( "glDepthRange" );
+ dllDepthRange( zNear, zFar );
+}
+
+static void APIENTRY logDisable(GLenum cap)
+{
+ fprintf( glw_state.log_fp, "glDisable( 0x%x )\n", cap );
+ dllDisable( cap );
+}
+
+static void APIENTRY logDisableClientState(GLenum array)
+{
+ SIG( "glDisableClientState" );
+ dllDisableClientState( array );
+}
+
+static void APIENTRY logDrawArrays(GLenum mode, GLint first, GLsizei count)
+{
+ SIG( "glDrawArrays" );
+ dllDrawArrays( mode, first, count );
+}
+
+static void APIENTRY logDrawBuffer(GLenum mode)
+{
+ SIG( "glDrawBuffer" );
+ dllDrawBuffer( mode );
+}
+
+static void APIENTRY logDrawElements(GLenum mode, GLsizei count, GLenum type, const void *indices)
+{
+ SIG( "glDrawElements" );
+ dllDrawElements( mode, count, type, indices );
+}
+
+static void APIENTRY logDrawPixels(GLsizei width, GLsizei height, GLenum format, GLenum type, const void *pixels)
+{
+ SIG( "glDrawPixels" );
+ dllDrawPixels( width, height, format, type, pixels );
+}
+
+static void APIENTRY logEdgeFlag(GLboolean flag)
+{
+ SIG( "glEdgeFlag" );
+ dllEdgeFlag( flag );
+}
+
+static void APIENTRY logEdgeFlagPointer(GLsizei stride, const void *pointer)
+{
+ SIG( "glEdgeFlagPointer" );
+ dllEdgeFlagPointer( stride, pointer );
+}
+
+static void APIENTRY logEdgeFlagv(const GLboolean *flag)
+{
+ SIG( "glEdgeFlagv" );
+ dllEdgeFlagv( flag );
+}
+
+static void APIENTRY logEnable(GLenum cap)
+{
+ fprintf( glw_state.log_fp, "glEnable( 0x%x )\n", cap );
+ dllEnable( cap );
+}
+
+static void APIENTRY logEnableClientState(GLenum array)
+{
+ SIG( "glEnableClientState" );
+ dllEnableClientState( array );
+}
+
+static void APIENTRY logEnd(void)
+{
+ SIG( "glEnd" );
+ dllEnd();
+}
+
+static void APIENTRY logEndList(void)
+{
+ SIG( "glEndList" );
+ dllEndList();
+}
+
+static void APIENTRY logEvalCoord1d(GLdouble u)
+{
+ SIG( "glEvalCoord1d" );
+ dllEvalCoord1d( u );
+}
+
+static void APIENTRY logEvalCoord1dv(const GLdouble *u)
+{
+ SIG( "glEvalCoord1dv" );
+ dllEvalCoord1dv( u );
+}
+
+static void APIENTRY logEvalCoord1f(GLfloat u)
+{
+ SIG( "glEvalCoord1f" );
+ dllEvalCoord1f( u );
+}
+
+static void APIENTRY logEvalCoord1fv(const GLfloat *u)
+{
+ SIG( "glEvalCoord1fv" );
+ dllEvalCoord1fv( u );
+}
+static void APIENTRY logEvalCoord2d(GLdouble u, GLdouble v)
+{
+ SIG( "glEvalCoord2d" );
+ dllEvalCoord2d( u, v );
+}
+static void APIENTRY logEvalCoord2dv(const GLdouble *u)
+{
+ SIG( "glEvalCoord2dv" );
+ dllEvalCoord2dv( u );
+}
+static void APIENTRY logEvalCoord2f(GLfloat u, GLfloat v)
+{
+ SIG( "glEvalCoord2f" );
+ dllEvalCoord2f( u, v );
+}
+static void APIENTRY logEvalCoord2fv(const GLfloat *u)
+{
+ SIG( "glEvalCoord2fv" );
+ dllEvalCoord2fv( u );
+}
+
+static void APIENTRY logEvalMesh1(GLenum mode, GLint i1, GLint i2)
+{
+ SIG( "glEvalMesh1" );
+ dllEvalMesh1( mode, i1, i2 );
+}
+static void APIENTRY logEvalMesh2(GLenum mode, GLint i1, GLint i2, GLint j1, GLint j2)
+{
+ SIG( "glEvalMesh2" );
+ dllEvalMesh2( mode, i1, i2, j1, j2 );
+}
+static void APIENTRY logEvalPoint1(GLint i)
+{
+ SIG( "glEvalPoint1" );
+ dllEvalPoint1( i );
+}
+static void APIENTRY logEvalPoint2(GLint i, GLint j)
+{
+ SIG( "glEvalPoint2" );
+ dllEvalPoint2( i, j );
+}
+
+static void APIENTRY logFeedbackBuffer(GLsizei size, GLenum type, GLfloat *buffer)
+{
+ SIG( "glFeedbackBuffer" );
+ dllFeedbackBuffer( size, type, buffer );
+}
+
+static void APIENTRY logFinish(void)
+{
+ SIG( "glFinish" );
+ dllFinish();
+}
+
+static void APIENTRY logFlush(void)
+{
+ SIG( "glFlush" );
+ dllFlush();
+}
+
+static void APIENTRY logFogf(GLenum pname, GLfloat param)
+{
+ SIG( "glFogf" );
+ dllFogf( pname, param );
+}
+
+static void APIENTRY logFogfv(GLenum pname, const GLfloat *params)
+{
+ SIG( "glFogfv" );
+ dllFogfv( pname, params );
+}
+
+static void APIENTRY logFogi(GLenum pname, GLint param)
+{
+ SIG( "glFogi" );
+ dllFogi( pname, param );
+}
+
+static void APIENTRY logFogiv(GLenum pname, const GLint *params)
+{
+ SIG( "glFogiv" );
+ dllFogiv( pname, params );
+}
+
+static void APIENTRY logFrontFace(GLenum mode)
+{
+ SIG( "glFrontFace" );
+ dllFrontFace( mode );
+}
+
+static void APIENTRY logFrustum(GLdouble left, GLdouble right, GLdouble bottom, GLdouble top, GLdouble zNear, GLdouble zFar)
+{
+ SIG( "glFrustum" );
+ dllFrustum( left, right, bottom, top, zNear, zFar );
+}
+
+static GLuint APIENTRY logGenLists(GLsizei range)
+{
+ SIG( "glGenLists" );
+ return dllGenLists( range );
+}
+
+static void APIENTRY logGenTextures(GLsizei n, GLuint *textures)
+{
+ SIG( "glGenTextures" );
+ dllGenTextures( n, textures );
+}
+
+static void APIENTRY logGetBooleanv(GLenum pname, GLboolean *params)
+{
+ SIG( "glGetBooleanv" );
+ dllGetBooleanv( pname, params );
+}
+
+static void APIENTRY logGetClipPlane(GLenum plane, GLdouble *equation)
+{
+ SIG( "glGetClipPlane" );
+ dllGetClipPlane( plane, equation );
+}
+
+static void APIENTRY logGetDoublev(GLenum pname, GLdouble *params)
+{
+ SIG( "glGetDoublev" );
+ dllGetDoublev( pname, params );
+}
+
+static GLenum APIENTRY logGetError(void)
+{
+ SIG( "glGetError" );
+ return dllGetError();
+}
+
+static void APIENTRY logGetFloatv(GLenum pname, GLfloat *params)
+{
+ SIG( "glGetFloatv" );
+ dllGetFloatv( pname, params );
+}
+
+static void APIENTRY logGetIntegerv(GLenum pname, GLint *params)
+{
+ SIG( "glGetIntegerv" );
+ dllGetIntegerv( pname, params );
+}
+
+static void APIENTRY logGetLightfv(GLenum light, GLenum pname, GLfloat *params)
+{
+ SIG( "glGetLightfv" );
+ dllGetLightfv( light, pname, params );
+}
+
+static void APIENTRY logGetLightiv(GLenum light, GLenum pname, GLint *params)
+{
+ SIG( "glGetLightiv" );
+ dllGetLightiv( light, pname, params );
+}
+
+static void APIENTRY logGetMapdv(GLenum target, GLenum query, GLdouble *v)
+{
+ SIG( "glGetMapdv" );
+ dllGetMapdv( target, query, v );
+}
+
+static void APIENTRY logGetMapfv(GLenum target, GLenum query, GLfloat *v)
+{
+ SIG( "glGetMapfv" );
+ dllGetMapfv( target, query, v );
+}
+
+static void APIENTRY logGetMapiv(GLenum target, GLenum query, GLint *v)
+{
+ SIG( "glGetMapiv" );
+ dllGetMapiv( target, query, v );
+}
+
+static void APIENTRY logGetMaterialfv(GLenum face, GLenum pname, GLfloat *params)
+{
+ SIG( "glGetMaterialfv" );
+ dllGetMaterialfv( face, pname, params );
+}
+
+static void APIENTRY logGetMaterialiv(GLenum face, GLenum pname, GLint *params)
+{
+ SIG( "glGetMaterialiv" );
+ dllGetMaterialiv( face, pname, params );
+}
+
+static void APIENTRY logGetPixelMapfv(GLenum map, GLfloat *values)
+{
+ SIG( "glGetPixelMapfv" );
+ dllGetPixelMapfv( map, values );
+}
+
+static void APIENTRY logGetPixelMapuiv(GLenum map, GLuint *values)
+{
+ SIG( "glGetPixelMapuiv" );
+ dllGetPixelMapuiv( map, values );
+}
+
+static void APIENTRY logGetPixelMapusv(GLenum map, GLushort *values)
+{
+ SIG( "glGetPixelMapusv" );
+ dllGetPixelMapusv( map, values );
+}
+
+static void APIENTRY logGetPointerv(GLenum pname, GLvoid* *params)
+{
+ SIG( "glGetPointerv" );
+ dllGetPointerv( pname, params );
+}
+
+static void APIENTRY logGetPolygonStipple(GLubyte *mask)
+{
+ SIG( "glGetPolygonStipple" );
+ dllGetPolygonStipple( mask );
+}
+
+static const GLubyte * APIENTRY logGetString(GLenum name)
+{
+ SIG( "glGetString" );
+ return dllGetString( name );
+}
+
+static void APIENTRY logGetTexEnvfv(GLenum target, GLenum pname, GLfloat *params)
+{
+ SIG( "glGetTexEnvfv" );
+ dllGetTexEnvfv( target, pname, params );
+}
+
+static void APIENTRY logGetTexEnviv(GLenum target, GLenum pname, GLint *params)
+{
+ SIG( "glGetTexEnviv" );
+ dllGetTexEnviv( target, pname, params );
+}
+
+static void APIENTRY logGetTexGendv(GLenum coord, GLenum pname, GLdouble *params)
+{
+ SIG( "glGetTexGendv" );
+ dllGetTexGendv( coord, pname, params );
+}
+
+static void APIENTRY logGetTexGenfv(GLenum coord, GLenum pname, GLfloat *params)
+{
+ SIG( "glGetTexGenfv" );
+ dllGetTexGenfv( coord, pname, params );
+}
+
+static void APIENTRY logGetTexGeniv(GLenum coord, GLenum pname, GLint *params)
+{
+ SIG( "glGetTexGeniv" );
+ dllGetTexGeniv( coord, pname, params );
+}
+
+static void APIENTRY logGetTexImage(GLenum target, GLint level, GLenum format, GLenum type, void *pixels)
+{
+ SIG( "glGetTexImage" );
+ dllGetTexImage( target, level, format, type, pixels );
+}
+static void APIENTRY logGetTexLevelParameterfv(GLenum target, GLint level, GLenum pname, GLfloat *params )
+{
+ SIG( "glGetTexLevelParameterfv" );
+ dllGetTexLevelParameterfv( target, level, pname, params );
+}
+
+static void APIENTRY logGetTexLevelParameteriv(GLenum target, GLint level, GLenum pname, GLint *params)
+{
+ SIG( "glGetTexLevelParameteriv" );
+ dllGetTexLevelParameteriv( target, level, pname, params );
+}
+
+static void APIENTRY logGetTexParameterfv(GLenum target, GLenum pname, GLfloat *params)
+{
+ SIG( "glGetTexParameterfv" );
+ dllGetTexParameterfv( target, pname, params );
+}
+
+static void APIENTRY logGetTexParameteriv(GLenum target, GLenum pname, GLint *params)
+{
+ SIG( "glGetTexParameteriv" );
+ dllGetTexParameteriv( target, pname, params );
+}
+
+static void APIENTRY logHint(GLenum target, GLenum mode)
+{
+ fprintf( glw_state.log_fp, "glHint( 0x%x, 0x%x )\n", target, mode );
+ dllHint( target, mode );
+}
+
+static void APIENTRY logIndexMask(GLuint mask)
+{
+ SIG( "glIndexMask" );
+ dllIndexMask( mask );
+}
+
+static void APIENTRY logIndexPointer(GLenum type, GLsizei stride, const void *pointer)
+{
+ SIG( "glIndexPointer" );
+ dllIndexPointer( type, stride, pointer );
+}
+
+static void APIENTRY logIndexd(GLdouble c)
+{
+ SIG( "glIndexd" );
+ dllIndexd( c );
+}
+
+static void APIENTRY logIndexdv(const GLdouble *c)
+{
+ SIG( "glIndexdv" );
+ dllIndexdv( c );
+}
+
+static void APIENTRY logIndexf(GLfloat c)
+{
+ SIG( "glIndexf" );
+ dllIndexf( c );
+}
+
+static void APIENTRY logIndexfv(const GLfloat *c)
+{
+ SIG( "glIndexfv" );
+ dllIndexfv( c );
+}
+
+static void APIENTRY logIndexi(GLint c)
+{
+ SIG( "glIndexi" );
+ dllIndexi( c );
+}
+
+static void APIENTRY logIndexiv(const GLint *c)
+{
+ SIG( "glIndexiv" );
+ dllIndexiv( c );
+}
+
+static void APIENTRY logIndexs(GLshort c)
+{
+ SIG( "glIndexs" );
+ dllIndexs( c );
+}
+
+static void APIENTRY logIndexsv(const GLshort *c)
+{
+ SIG( "glIndexsv" );
+ dllIndexsv( c );
+}
+
+static void APIENTRY logIndexub(GLubyte c)
+{
+ SIG( "glIndexub" );
+ dllIndexub( c );
+}
+
+static void APIENTRY logIndexubv(const GLubyte *c)
+{
+ SIG( "glIndexubv" );
+ dllIndexubv( c );
+}
+
+static void APIENTRY logInitNames(void)
+{
+ SIG( "glInitNames" );
+ dllInitNames();
+}
+
+static void APIENTRY logInterleavedArrays(GLenum format, GLsizei stride, const void *pointer)
+{
+ SIG( "glInterleavedArrays" );
+ dllInterleavedArrays( format, stride, pointer );
+}
+
+static GLboolean APIENTRY logIsEnabled(GLenum cap)
+{
+ SIG( "glIsEnabled" );
+ return dllIsEnabled( cap );
+}
+static GLboolean APIENTRY logIsList(GLuint list)
+{
+ SIG( "glIsList" );
+ return dllIsList( list );
+}
+static GLboolean APIENTRY logIsTexture(GLuint texture)
+{
+ SIG( "glIsTexture" );
+ return dllIsTexture( texture );
+}
+
+static void APIENTRY logLightModelf(GLenum pname, GLfloat param)
+{
+ SIG( "glLightModelf" );
+ dllLightModelf( pname, param );
+}
+
+static void APIENTRY logLightModelfv(GLenum pname, const GLfloat *params)
+{
+ SIG( "glLightModelfv" );
+ dllLightModelfv( pname, params );
+}
+
+static void APIENTRY logLightModeli(GLenum pname, GLint param)
+{
+ SIG( "glLightModeli" );
+ dllLightModeli( pname, param );
+
+}
+
+static void APIENTRY logLightModeliv(GLenum pname, const GLint *params)
+{
+ SIG( "glLightModeliv" );
+ dllLightModeliv( pname, params );
+}
+
+static void APIENTRY logLightf(GLenum light, GLenum pname, GLfloat param)
+{
+ SIG( "glLightf" );
+ dllLightf( light, pname, param );
+}
+
+static void APIENTRY logLightfv(GLenum light, GLenum pname, const GLfloat *params)
+{
+ SIG( "glLightfv" );
+ dllLightfv( light, pname, params );
+}
+
+static void APIENTRY logLighti(GLenum light, GLenum pname, GLint param)
+{
+ SIG( "glLighti" );
+ dllLighti( light, pname, param );
+}
+
+static void APIENTRY logLightiv(GLenum light, GLenum pname, const GLint *params)
+{
+ SIG( "glLightiv" );
+ dllLightiv( light, pname, params );
+}
+
+static void APIENTRY logLineStipple(GLint factor, GLushort pattern)
+{
+ SIG( "glLineStipple" );
+ dllLineStipple( factor, pattern );
+}
+
+static void APIENTRY logLineWidth(GLfloat width)
+{
+ SIG( "glLineWidth" );
+ dllLineWidth( width );
+}
+
+static void APIENTRY logListBase(GLuint base)
+{
+ SIG( "glListBase" );
+ dllListBase( base );
+}
+
+static void APIENTRY logLoadIdentity(void)
+{
+ SIG( "glLoadIdentity" );
+ dllLoadIdentity();
+}
+
+static void APIENTRY logLoadMatrixd(const GLdouble *m)
+{
+ SIG( "glLoadMatrixd" );
+ dllLoadMatrixd( m );
+}
+
+static void APIENTRY logLoadMatrixf(const GLfloat *m)
+{
+ SIG( "glLoadMatrixf" );
+ dllLoadMatrixf( m );
+}
+
+static void APIENTRY logLoadName(GLuint name)
+{
+ SIG( "glLoadName" );
+ dllLoadName( name );
+}
+
+static void APIENTRY logLogicOp(GLenum opcode)
+{
+ SIG( "glLogicOp" );
+ dllLogicOp( opcode );
+}
+
+static void APIENTRY logMap1d(GLenum target, GLdouble u1, GLdouble u2, GLint stride, GLint order, const GLdouble *points)
+{
+ SIG( "glMap1d" );
+ dllMap1d( target, u1, u2, stride, order, points );
+}
+
+static void APIENTRY logMap1f(GLenum target, GLfloat u1, GLfloat u2, GLint stride, GLint order, const GLfloat *points)
+{
+ SIG( "glMap1f" );
+ dllMap1f( target, u1, u2, stride, order, points );
+}
+
+static void APIENTRY logMap2d(GLenum target, GLdouble u1, GLdouble u2, GLint ustride, GLint uorder, GLdouble v1, GLdouble v2, GLint vstride, GLint vorder, const GLdouble *points)
+{
+ SIG( "glMap2d" );
+ dllMap2d( target, u1, u2, ustride, uorder, v1, v2, vstride, vorder, points );
+}
+
+static void APIENTRY logMap2f(GLenum target, GLfloat u1, GLfloat u2, GLint ustride, GLint uorder, GLfloat v1, GLfloat v2, GLint vstride, GLint vorder, const GLfloat *points)
+{
+ SIG( "glMap2f" );
+ dllMap2f( target, u1, u2, ustride, uorder, v1, v2, vstride, vorder, points );
+}
+
+static void APIENTRY logMapGrid1d(GLint un, GLdouble u1, GLdouble u2)
+{
+ SIG( "glMapGrid1d" );
+ dllMapGrid1d( un, u1, u2 );
+}
+
+static void APIENTRY logMapGrid1f(GLint un, GLfloat u1, GLfloat u2)
+{
+ SIG( "glMapGrid1f" );
+ dllMapGrid1f( un, u1, u2 );
+}
+
+static void APIENTRY logMapGrid2d(GLint un, GLdouble u1, GLdouble u2, GLint vn, GLdouble v1, GLdouble v2)
+{
+ SIG( "glMapGrid2d" );
+ dllMapGrid2d( un, u1, u2, vn, v1, v2 );
+}
+static void APIENTRY logMapGrid2f(GLint un, GLfloat u1, GLfloat u2, GLint vn, GLfloat v1, GLfloat v2)
+{
+ SIG( "glMapGrid2f" );
+ dllMapGrid2f( un, u1, u2, vn, v1, v2 );
+}
+static void APIENTRY logMaterialf(GLenum face, GLenum pname, GLfloat param)
+{
+ SIG( "glMaterialf" );
+ dllMaterialf( face, pname, param );
+}
+static void APIENTRY logMaterialfv(GLenum face, GLenum pname, const GLfloat *params)
+{
+ SIG( "glMaterialfv" );
+ dllMaterialfv( face, pname, params );
+}
+
+static void APIENTRY logMateriali(GLenum face, GLenum pname, GLint param)
+{
+ SIG( "glMateriali" );
+ dllMateriali( face, pname, param );
+}
+
+static void APIENTRY logMaterialiv(GLenum face, GLenum pname, const GLint *params)
+{
+ SIG( "glMaterialiv" );
+ dllMaterialiv( face, pname, params );
+}
+
+static void APIENTRY logMatrixMode(GLenum mode)
+{
+ SIG( "glMatrixMode" );
+ dllMatrixMode( mode );
+}
+
+static void APIENTRY logMultMatrixd(const GLdouble *m)
+{
+ SIG( "glMultMatrixd" );
+ dllMultMatrixd( m );
+}
+
+static void APIENTRY logMultMatrixf(const GLfloat *m)
+{
+ SIG( "glMultMatrixf" );
+ dllMultMatrixf( m );
+}
+
+static void APIENTRY logNewList(GLuint list, GLenum mode)
+{
+ SIG( "glNewList" );
+ dllNewList( list, mode );
+}
+
+static void APIENTRY logNormal3b(GLbyte nx, GLbyte ny, GLbyte nz)
+{
+ SIG ("glNormal3b" );
+ dllNormal3b( nx, ny, nz );
+}
+
+static void APIENTRY logNormal3bv(const GLbyte *v)
+{
+ SIG( "glNormal3bv" );
+ dllNormal3bv( v );
+}
+
+static void APIENTRY logNormal3d(GLdouble nx, GLdouble ny, GLdouble nz)
+{
+ SIG( "glNormal3d" );
+ dllNormal3d( nx, ny, nz );
+}
+
+static void APIENTRY logNormal3dv(const GLdouble *v)
+{
+ SIG( "glNormal3dv" );
+ dllNormal3dv( v );
+}
+
+static void APIENTRY logNormal3f(GLfloat nx, GLfloat ny, GLfloat nz)
+{
+ SIG( "glNormal3f" );
+ dllNormal3f( nx, ny, nz );
+}
+
+static void APIENTRY logNormal3fv(const GLfloat *v)
+{
+ SIG( "glNormal3fv" );
+ dllNormal3fv( v );
+}
+static void APIENTRY logNormal3i(GLint nx, GLint ny, GLint nz)
+{
+ SIG( "glNormal3i" );
+ dllNormal3i( nx, ny, nz );
+}
+static void APIENTRY logNormal3iv(const GLint *v)
+{
+ SIG( "glNormal3iv" );
+ dllNormal3iv( v );
+}
+static void APIENTRY logNormal3s(GLshort nx, GLshort ny, GLshort nz)
+{
+ SIG( "glNormal3s" );
+ dllNormal3s( nx, ny, nz );
+}
+static void APIENTRY logNormal3sv(const GLshort *v)
+{
+ SIG( "glNormal3sv" );
+ dllNormal3sv( v );
+}
+static void APIENTRY logNormalPointer(GLenum type, GLsizei stride, const void *pointer)
+{
+ SIG( "glNormalPointer" );
+ dllNormalPointer( type, stride, pointer );
+}
+static void APIENTRY logOrtho(GLdouble left, GLdouble right, GLdouble bottom, GLdouble top, GLdouble zNear, GLdouble zFar)
+{
+ SIG( "glOrtho" );
+ dllOrtho( left, right, bottom, top, zNear, zFar );
+}
+
+static void APIENTRY logPassThrough(GLfloat token)
+{
+ SIG( "glPassThrough" );
+ dllPassThrough( token );
+}
+
+static void APIENTRY logPixelMapfv(GLenum map, GLsizei mapsize, const GLfloat *values)
+{
+ SIG( "glPixelMapfv" );
+ dllPixelMapfv( map, mapsize, values );
+}
+
+static void APIENTRY logPixelMapuiv(GLenum map, GLsizei mapsize, const GLuint *values)
+{
+ SIG( "glPixelMapuiv" );
+ dllPixelMapuiv( map, mapsize, values );
+}
+
+static void APIENTRY logPixelMapusv(GLenum map, GLsizei mapsize, const GLushort *values)
+{
+ SIG( "glPixelMapusv" );
+ dllPixelMapusv( map, mapsize, values );
+}
+static void APIENTRY logPixelStoref(GLenum pname, GLfloat param)
+{
+ SIG( "glPixelStoref" );
+ dllPixelStoref( pname, param );
+}
+static void APIENTRY logPixelStorei(GLenum pname, GLint param)
+{
+ SIG( "glPixelStorei" );
+ dllPixelStorei( pname, param );
+}
+static void APIENTRY logPixelTransferf(GLenum pname, GLfloat param)
+{
+ SIG( "glPixelTransferf" );
+ dllPixelTransferf( pname, param );
+}
+
+static void APIENTRY logPixelTransferi(GLenum pname, GLint param)
+{
+ SIG( "glPixelTransferi" );
+ dllPixelTransferi( pname, param );
+}
+
+static void APIENTRY logPixelZoom(GLfloat xfactor, GLfloat yfactor)
+{
+ SIG( "glPixelZoom" );
+ dllPixelZoom( xfactor, yfactor );
+}
+
+static void APIENTRY logPointSize(GLfloat size)
+{
+ SIG( "glPointSize" );
+ dllPointSize( size );
+}
+
+static void APIENTRY logPolygonMode(GLenum face, GLenum mode)
+{
+ fprintf( glw_state.log_fp, "glPolygonMode( 0x%x, 0x%x )\n", face, mode );
+ dllPolygonMode( face, mode );
+}
+
+static void APIENTRY logPolygonOffset(GLfloat factor, GLfloat units)
+{
+ SIG( "glPolygonOffset" );
+ dllPolygonOffset( factor, units );
+}
+static void APIENTRY logPolygonStipple(const GLubyte *mask )
+{
+ SIG( "glPolygonStipple" );
+ dllPolygonStipple( mask );
+}
+static void APIENTRY logPopAttrib(void)
+{
+ SIG( "glPopAttrib" );
+ dllPopAttrib();
+}
+
+static void APIENTRY logPopClientAttrib(void)
+{
+ SIG( "glPopClientAttrib" );
+ dllPopClientAttrib();
+}
+
+static void APIENTRY logPopMatrix(void)
+{
+ SIG( "glPopMatrix" );
+ dllPopMatrix();
+}
+
+static void APIENTRY logPopName(void)
+{
+ SIG( "glPopName" );
+ dllPopName();
+}
+
+static void APIENTRY logPrioritizeTextures(GLsizei n, const GLuint *textures, const GLclampf *priorities)
+{
+ SIG( "glPrioritizeTextures" );
+ dllPrioritizeTextures( n, textures, priorities );
+}
+
+static void APIENTRY logPushAttrib(GLbitfield mask)
+{
+ SIG( "glPushAttrib" );
+ dllPushAttrib( mask );
+}
+
+static void APIENTRY logPushClientAttrib(GLbitfield mask)
+{
+ SIG( "glPushClientAttrib" );
+ dllPushClientAttrib( mask );
+}
+
+static void APIENTRY logPushMatrix(void)
+{
+ SIG( "glPushMatrix" );
+ dllPushMatrix();
+}
+
+static void APIENTRY logPushName(GLuint name)
+{
+ SIG( "glPushName" );
+ dllPushName( name );
+}
+
+static void APIENTRY logRasterPos2d(GLdouble x, GLdouble y)
+{
+ SIG ("glRasterPot2d" );
+ dllRasterPos2d( x, y );
+}
+
+static void APIENTRY logRasterPos2dv(const GLdouble *v)
+{
+ SIG( "glRasterPos2dv" );
+ dllRasterPos2dv( v );
+}
+
+static void APIENTRY logRasterPos2f(GLfloat x, GLfloat y)
+{
+ SIG( "glRasterPos2f" );
+ dllRasterPos2f( x, y );
+}
+static void APIENTRY logRasterPos2fv(const GLfloat *v)
+{
+ SIG( "glRasterPos2dv" );
+ dllRasterPos2fv( v );
+}
+static void APIENTRY logRasterPos2i(GLint x, GLint y)
+{
+ SIG( "glRasterPos2if" );
+ dllRasterPos2i( x, y );
+}
+static void APIENTRY logRasterPos2iv(const GLint *v)
+{
+ SIG( "glRasterPos2iv" );
+ dllRasterPos2iv( v );
+}
+static void APIENTRY logRasterPos2s(GLshort x, GLshort y)
+{
+ SIG( "glRasterPos2s" );
+ dllRasterPos2s( x, y );
+}
+static void APIENTRY logRasterPos2sv(const GLshort *v)
+{
+ SIG( "glRasterPos2sv" );
+ dllRasterPos2sv( v );
+}
+static void APIENTRY logRasterPos3d(GLdouble x, GLdouble y, GLdouble z)
+{
+ SIG( "glRasterPos3d" );
+ dllRasterPos3d( x, y, z );
+}
+static void APIENTRY logRasterPos3dv(const GLdouble *v)
+{
+ SIG( "glRasterPos3dv" );
+ dllRasterPos3dv( v );
+}
+static void APIENTRY logRasterPos3f(GLfloat x, GLfloat y, GLfloat z)
+{
+ SIG( "glRasterPos3f" );
+ dllRasterPos3f( x, y, z );
+}
+static void APIENTRY logRasterPos3fv(const GLfloat *v)
+{
+ SIG( "glRasterPos3fv" );
+ dllRasterPos3fv( v );
+}
+static void APIENTRY logRasterPos3i(GLint x, GLint y, GLint z)
+{
+ SIG( "glRasterPos3i" );
+ dllRasterPos3i( x, y, z );
+}
+static void APIENTRY logRasterPos3iv(const GLint *v)
+{
+ SIG( "glRasterPos3iv" );
+ dllRasterPos3iv( v );
+}
+static void APIENTRY logRasterPos3s(GLshort x, GLshort y, GLshort z)
+{
+ SIG( "glRasterPos3s" );
+ dllRasterPos3s( x, y, z );
+}
+static void APIENTRY logRasterPos3sv(const GLshort *v)
+{
+ SIG( "glRasterPos3sv" );
+ dllRasterPos3sv( v );
+}
+static void APIENTRY logRasterPos4d(GLdouble x, GLdouble y, GLdouble z, GLdouble w)
+{
+ SIG( "glRasterPos4d" );
+ dllRasterPos4d( x, y, z, w );
+}
+static void APIENTRY logRasterPos4dv(const GLdouble *v)
+{
+ SIG( "glRasterPos4dv" );
+ dllRasterPos4dv( v );
+}
+static void APIENTRY logRasterPos4f(GLfloat x, GLfloat y, GLfloat z, GLfloat w)
+{
+ SIG( "glRasterPos4f" );
+ dllRasterPos4f( x, y, z, w );
+}
+static void APIENTRY logRasterPos4fv(const GLfloat *v)
+{
+ SIG( "glRasterPos4fv" );
+ dllRasterPos4fv( v );
+}
+static void APIENTRY logRasterPos4i(GLint x, GLint y, GLint z, GLint w)
+{
+ SIG( "glRasterPos4i" );
+ dllRasterPos4i( x, y, z, w );
+}
+static void APIENTRY logRasterPos4iv(const GLint *v)
+{
+ SIG( "glRasterPos4iv" );
+ dllRasterPos4iv( v );
+}
+static void APIENTRY logRasterPos4s(GLshort x, GLshort y, GLshort z, GLshort w)
+{
+ SIG( "glRasterPos4s" );
+ dllRasterPos4s( x, y, z, w );
+}
+static void APIENTRY logRasterPos4sv(const GLshort *v)
+{
+ SIG( "glRasterPos4sv" );
+ dllRasterPos4sv( v );
+}
+static void APIENTRY logReadBuffer(GLenum mode)
+{
+ SIG( "glReadBuffer" );
+ dllReadBuffer( mode );
+}
+static void APIENTRY logReadPixels(GLint x, GLint y, GLsizei width, GLsizei height, GLenum format, GLenum type, void *pixels)
+{
+ SIG( "glReadPixels" );
+ dllReadPixels( x, y, width, height, format, type, pixels );
+}
+
+static void APIENTRY logRectd(GLdouble x1, GLdouble y1, GLdouble x2, GLdouble y2)
+{
+ SIG( "glRectd" );
+ dllRectd( x1, y1, x2, y2 );
+}
+
+static void APIENTRY logRectdv(const GLdouble *v1, const GLdouble *v2)
+{
+ SIG( "glRectdv" );
+ dllRectdv( v1, v2 );
+}
+
+static void APIENTRY logRectf(GLfloat x1, GLfloat y1, GLfloat x2, GLfloat y2)
+{
+ SIG( "glRectf" );
+ dllRectf( x1, y1, x2, y2 );
+}
+
+static void APIENTRY logRectfv(const GLfloat *v1, const GLfloat *v2)
+{
+ SIG( "glRectfv" );
+ dllRectfv( v1, v2 );
+}
+static void APIENTRY logRecti(GLint x1, GLint y1, GLint x2, GLint y2)
+{
+ SIG( "glRecti" );
+ dllRecti( x1, y1, x2, y2 );
+}
+static void APIENTRY logRectiv(const GLint *v1, const GLint *v2)
+{
+ SIG( "glRectiv" );
+ dllRectiv( v1, v2 );
+}
+static void APIENTRY logRects(GLshort x1, GLshort y1, GLshort x2, GLshort y2)
+{
+ SIG( "glRects" );
+ dllRects( x1, y1, x2, y2 );
+}
+static void APIENTRY logRectsv(const GLshort *v1, const GLshort *v2)
+{
+ SIG( "glRectsv" );
+ dllRectsv( v1, v2 );
+}
+static GLint APIENTRY logRenderMode(GLenum mode)
+{
+ SIG( "glRenderMode" );
+ return dllRenderMode( mode );
+}
+static void APIENTRY logRotated(GLdouble angle, GLdouble x, GLdouble y, GLdouble z)
+{
+ SIG( "glRotated" );
+ dllRotated( angle, x, y, z );
+}
+
+static void APIENTRY logRotatef(GLfloat angle, GLfloat x, GLfloat y, GLfloat z)
+{
+ SIG( "glRotatef" );
+ dllRotatef( angle, x, y, z );
+}
+
+static void APIENTRY logScaled(GLdouble x, GLdouble y, GLdouble z)
+{
+ SIG( "glScaled" );
+ dllScaled( x, y, z );
+}
+
+static void APIENTRY logScalef(GLfloat x, GLfloat y, GLfloat z)
+{
+ SIG( "glScalef" );
+ dllScalef( x, y, z );
+}
+
+static void APIENTRY logScissor(GLint x, GLint y, GLsizei width, GLsizei height)
+{
+ SIG( "glScissor" );
+ dllScissor( x, y, width, height );
+}
+
+static void APIENTRY logSelectBuffer(GLsizei size, GLuint *buffer)
+{
+ SIG( "glSelectBuffer" );
+ dllSelectBuffer( size, buffer );
+}
+
+static void APIENTRY logShadeModel(GLenum mode)
+{
+ SIG( "glShadeModel" );
+ dllShadeModel( mode );
+}
+
+static void APIENTRY logStencilFunc(GLenum func, GLint ref, GLuint mask)
+{
+ SIG( "glStencilFunc" );
+ dllStencilFunc( func, ref, mask );
+}
+
+static void APIENTRY logStencilMask(GLuint mask)
+{
+ SIG( "glStencilMask" );
+ dllStencilMask( mask );
+}
+
+static void APIENTRY logStencilOp(GLenum fail, GLenum zfail, GLenum zpass)
+{
+ SIG( "glStencilOp" );
+ dllStencilOp( fail, zfail, zpass );
+}
+
+static void APIENTRY logTexCoord1d(GLdouble s)
+{
+ SIG( "glTexCoord1d" );
+ dllTexCoord1d( s );
+}
+
+static void APIENTRY logTexCoord1dv(const GLdouble *v)
+{
+ SIG( "glTexCoord1dv" );
+ dllTexCoord1dv( v );
+}
+
+static void APIENTRY logTexCoord1f(GLfloat s)
+{
+ SIG( "glTexCoord1f" );
+ dllTexCoord1f( s );
+}
+static void APIENTRY logTexCoord1fv(const GLfloat *v)
+{
+ SIG( "glTexCoord1fv" );
+ dllTexCoord1fv( v );
+}
+static void APIENTRY logTexCoord1i(GLint s)
+{
+ SIG( "glTexCoord1i" );
+ dllTexCoord1i( s );
+}
+static void APIENTRY logTexCoord1iv(const GLint *v)
+{
+ SIG( "glTexCoord1iv" );
+ dllTexCoord1iv( v );
+}
+static void APIENTRY logTexCoord1s(GLshort s)
+{
+ SIG( "glTexCoord1s" );
+ dllTexCoord1s( s );
+}
+static void APIENTRY logTexCoord1sv(const GLshort *v)
+{
+ SIG( "glTexCoord1sv" );
+ dllTexCoord1sv( v );
+}
+static void APIENTRY logTexCoord2d(GLdouble s, GLdouble t)
+{
+ SIG( "glTexCoord2d" );
+ dllTexCoord2d( s, t );
+}
+
+static void APIENTRY logTexCoord2dv(const GLdouble *v)
+{
+ SIG( "glTexCoord2dv" );
+ dllTexCoord2dv( v );
+}
+static void APIENTRY logTexCoord2f(GLfloat s, GLfloat t)
+{
+ SIG( "glTexCoord2f" );
+ dllTexCoord2f( s, t );
+}
+static void APIENTRY logTexCoord2fv(const GLfloat *v)
+{
+ SIG( "glTexCoord2fv" );
+ dllTexCoord2fv( v );
+}
+static void APIENTRY logTexCoord2i(GLint s, GLint t)
+{
+ SIG( "glTexCoord2i" );
+ dllTexCoord2i( s, t );
+}
+static void APIENTRY logTexCoord2iv(const GLint *v)
+{
+ SIG( "glTexCoord2iv" );
+ dllTexCoord2iv( v );
+}
+static void APIENTRY logTexCoord2s(GLshort s, GLshort t)
+{
+ SIG( "glTexCoord2s" );
+ dllTexCoord2s( s, t );
+}
+static void APIENTRY logTexCoord2sv(const GLshort *v)
+{
+ SIG( "glTexCoord2sv" );
+ dllTexCoord2sv( v );
+}
+static void APIENTRY logTexCoord3d(GLdouble s, GLdouble t, GLdouble r)
+{
+ SIG( "glTexCoord3d" );
+ dllTexCoord3d( s, t, r );
+}
+static void APIENTRY logTexCoord3dv(const GLdouble *v)
+{
+ SIG( "glTexCoord3dv" );
+ dllTexCoord3dv( v );
+}
+static void APIENTRY logTexCoord3f(GLfloat s, GLfloat t, GLfloat r)
+{
+ SIG( "glTexCoord3f" );
+ dllTexCoord3f( s, t, r );
+}
+static void APIENTRY logTexCoord3fv(const GLfloat *v)
+{
+ SIG( "glTexCoord3fv" );
+ dllTexCoord3fv( v );
+}
+static void APIENTRY logTexCoord3i(GLint s, GLint t, GLint r)
+{
+ SIG( "glTexCoord3i" );
+ dllTexCoord3i( s, t, r );
+}
+static void APIENTRY logTexCoord3iv(const GLint *v)
+{
+ SIG( "glTexCoord3iv" );
+ dllTexCoord3iv( v );
+}
+static void APIENTRY logTexCoord3s(GLshort s, GLshort t, GLshort r)
+{
+ SIG( "glTexCoord3s" );
+ dllTexCoord3s( s, t, r );
+}
+static void APIENTRY logTexCoord3sv(const GLshort *v)
+{
+ SIG( "glTexCoord3sv" );
+ dllTexCoord3sv( v );
+}
+static void APIENTRY logTexCoord4d(GLdouble s, GLdouble t, GLdouble r, GLdouble q)
+{
+ SIG( "glTexCoord4d" );
+ dllTexCoord4d( s, t, r, q );
+}
+static void APIENTRY logTexCoord4dv(const GLdouble *v)
+{
+ SIG( "glTexCoord4dv" );
+ dllTexCoord4dv( v );
+}
+static void APIENTRY logTexCoord4f(GLfloat s, GLfloat t, GLfloat r, GLfloat q)
+{
+ SIG( "glTexCoord4f" );
+ dllTexCoord4f( s, t, r, q );
+}
+static void APIENTRY logTexCoord4fv(const GLfloat *v)
+{
+ SIG( "glTexCoord4fv" );
+ dllTexCoord4fv( v );
+}
+static void APIENTRY logTexCoord4i(GLint s, GLint t, GLint r, GLint q)
+{
+ SIG( "glTexCoord4i" );
+ dllTexCoord4i( s, t, r, q );
+}
+static void APIENTRY logTexCoord4iv(const GLint *v)
+{
+ SIG( "glTexCoord4iv" );
+ dllTexCoord4iv( v );
+}
+static void APIENTRY logTexCoord4s(GLshort s, GLshort t, GLshort r, GLshort q)
+{
+ SIG( "glTexCoord4s" );
+ dllTexCoord4s( s, t, r, q );
+}
+static void APIENTRY logTexCoord4sv(const GLshort *v)
+{
+ SIG( "glTexCoord4sv" );
+ dllTexCoord4sv( v );
+}
+static void APIENTRY logTexCoordPointer(GLint size, GLenum type, GLsizei stride, const void *pointer)
+{
+ SIG( "glTexCoordPointer" );
+ dllTexCoordPointer( size, type, stride, pointer );
+}
+
+static void APIENTRY logTexEnvf(GLenum target, GLenum pname, GLfloat param)
+{
+ fprintf( glw_state.log_fp, "glTexEnvf( 0x%x, 0x%x, %f )\n", target, pname, param );
+ dllTexEnvf( target, pname, param );
+}
+
+static void APIENTRY logTexEnvfv(GLenum target, GLenum pname, const GLfloat *params)
+{
+ SIG( "glTexEnvfv" );
+ dllTexEnvfv( target, pname, params );
+}
+
+static void APIENTRY logTexEnvi(GLenum target, GLenum pname, GLint param)
+{
+ fprintf( glw_state.log_fp, "glTexEnvi( 0x%x, 0x%x, 0x%x )\n", target, pname, param );
+ dllTexEnvi( target, pname, param );
+}
+static void APIENTRY logTexEnviv(GLenum target, GLenum pname, const GLint *params)
+{
+ SIG( "glTexEnviv" );
+ dllTexEnviv( target, pname, params );
+}
+
+static void APIENTRY logTexGend(GLenum coord, GLenum pname, GLdouble param)
+{
+ SIG( "glTexGend" );
+ dllTexGend( coord, pname, param );
+}
+
+static void APIENTRY logTexGendv(GLenum coord, GLenum pname, const GLdouble *params)
+{
+ SIG( "glTexGendv" );
+ dllTexGendv( coord, pname, params );
+}
+
+static void APIENTRY logTexGenf(GLenum coord, GLenum pname, GLfloat param)
+{
+ SIG( "glTexGenf" );
+ dllTexGenf( coord, pname, param );
+}
+static void APIENTRY logTexGenfv(GLenum coord, GLenum pname, const GLfloat *params)
+{
+ SIG( "glTexGenfv" );
+ dllTexGenfv( coord, pname, params );
+}
+static void APIENTRY logTexGeni(GLenum coord, GLenum pname, GLint param)
+{
+ SIG( "glTexGeni" );
+ dllTexGeni( coord, pname, param );
+}
+static void APIENTRY logTexGeniv(GLenum coord, GLenum pname, const GLint *params)
+{
+ SIG( "glTexGeniv" );
+ dllTexGeniv( coord, pname, params );
+}
+static void APIENTRY logTexImage1D(GLenum target, GLint level, GLint internalformat, GLsizei width, GLint border, GLenum format, GLenum type, const void *pixels)
+{
+ SIG( "glTexImage1D" );
+ dllTexImage1D( target, level, internalformat, width, border, format, type, pixels );
+}
+static void APIENTRY logTexImage2D(GLenum target, GLint level, GLint internalformat, GLsizei width, GLsizei height, GLint border, GLenum format, GLenum type, const void *pixels)
+{
+ SIG( "glTexImage2D" );
+ dllTexImage2D( target, level, internalformat, width, height, border, format, type, pixels );
+}
+
+static void APIENTRY logTexParameterf(GLenum target, GLenum pname, GLfloat param)
+{
+ fprintf( glw_state.log_fp, "glTexParameterf( 0x%x, 0x%x, %f )\n", target, pname, param );
+ dllTexParameterf( target, pname, param );
+}
+
+static void APIENTRY logTexParameterfv(GLenum target, GLenum pname, const GLfloat *params)
+{
+ SIG( "glTexParameterfv" );
+ dllTexParameterfv( target, pname, params );
+}
+static void APIENTRY logTexParameteri(GLenum target, GLenum pname, GLint param)
+{
+ fprintf( glw_state.log_fp, "glTexParameteri( 0x%x, 0x%x, 0x%x )\n", target, pname, param );
+ dllTexParameteri( target, pname, param );
+}
+static void APIENTRY logTexParameteriv(GLenum target, GLenum pname, const GLint *params)
+{
+ SIG( "glTexParameteriv" );
+ dllTexParameteriv( target, pname, params );
+}
+static void APIENTRY logTexSubImage1D(GLenum target, GLint level, GLint xoffset, GLsizei width, GLenum format, GLenum type, const void *pixels)
+{
+ SIG( "glTexSubImage1D" );
+ dllTexSubImage1D( target, level, xoffset, width, format, type, pixels );
+}
+static void APIENTRY logTexSubImage2D(GLenum target, GLint level, GLint xoffset, GLint yoffset, GLsizei width, GLsizei height, GLenum format, GLenum type, const void *pixels)
+{
+ SIG( "glTexSubImage2D" );
+ dllTexSubImage2D( target, level, xoffset, yoffset, width, height, format, type, pixels );
+}
+static void APIENTRY logTranslated(GLdouble x, GLdouble y, GLdouble z)
+{
+ SIG( "glTranslated" );
+ dllTranslated( x, y, z );
+}
+
+static void APIENTRY logTranslatef(GLfloat x, GLfloat y, GLfloat z)
+{
+ SIG( "glTranslatef" );
+ dllTranslatef( x, y, z );
+}
+
+static void APIENTRY logVertex2d(GLdouble x, GLdouble y)
+{
+ SIG( "glVertex2d" );
+ dllVertex2d( x, y );
+}
+
+static void APIENTRY logVertex2dv(const GLdouble *v)
+{
+ SIG( "glVertex2dv" );
+ dllVertex2dv( v );
+}
+static void APIENTRY logVertex2f(GLfloat x, GLfloat y)
+{
+ SIG( "glVertex2f" );
+ dllVertex2f( x, y );
+}
+static void APIENTRY logVertex2fv(const GLfloat *v)
+{
+ SIG( "glVertex2fv" );
+ dllVertex2fv( v );
+}
+static void APIENTRY logVertex2i(GLint x, GLint y)
+{
+ SIG( "glVertex2i" );
+ dllVertex2i( x, y );
+}
+static void APIENTRY logVertex2iv(const GLint *v)
+{
+ SIG( "glVertex2iv" );
+ dllVertex2iv( v );
+}
+static void APIENTRY logVertex2s(GLshort x, GLshort y)
+{
+ SIG( "glVertex2s" );
+ dllVertex2s( x, y );
+}
+static void APIENTRY logVertex2sv(const GLshort *v)
+{
+ SIG( "glVertex2sv" );
+ dllVertex2sv( v );
+}
+static void APIENTRY logVertex3d(GLdouble x, GLdouble y, GLdouble z)
+{
+ SIG( "glVertex3d" );
+ dllVertex3d( x, y, z );
+}
+static void APIENTRY logVertex3dv(const GLdouble *v)
+{
+ SIG( "glVertex3dv" );
+ dllVertex3dv( v );
+}
+static void APIENTRY logVertex3f(GLfloat x, GLfloat y, GLfloat z)
+{
+ SIG( "glVertex3f" );
+ dllVertex3f( x, y, z );
+}
+static void APIENTRY logVertex3fv(const GLfloat *v)
+{
+ SIG( "glVertex3fv" );
+ dllVertex3fv( v );
+}
+static void APIENTRY logVertex3i(GLint x, GLint y, GLint z)
+{
+ SIG( "glVertex3i" );
+ dllVertex3i( x, y, z );
+}
+static void APIENTRY logVertex3iv(const GLint *v)
+{
+ SIG( "glVertex3iv" );
+ dllVertex3iv( v );
+}
+static void APIENTRY logVertex3s(GLshort x, GLshort y, GLshort z)
+{
+ SIG( "glVertex3s" );
+ dllVertex3s( x, y, z );
+}
+static void APIENTRY logVertex3sv(const GLshort *v)
+{
+ SIG( "glVertex3sv" );
+ dllVertex3sv( v );
+}
+static void APIENTRY logVertex4d(GLdouble x, GLdouble y, GLdouble z, GLdouble w)
+{
+ SIG( "glVertex4d" );
+ dllVertex4d( x, y, z, w );
+}
+static void APIENTRY logVertex4dv(const GLdouble *v)
+{
+ SIG( "glVertex4dv" );
+ dllVertex4dv( v );
+}
+static void APIENTRY logVertex4f(GLfloat x, GLfloat y, GLfloat z, GLfloat w)
+{
+ SIG( "glVertex4f" );
+ dllVertex4f( x, y, z, w );
+}
+static void APIENTRY logVertex4fv(const GLfloat *v)
+{
+ SIG( "glVertex4fv" );
+ dllVertex4fv( v );
+}
+static void APIENTRY logVertex4i(GLint x, GLint y, GLint z, GLint w)
+{
+ SIG( "glVertex4i" );
+ dllVertex4i( x, y, z, w );
+}
+static void APIENTRY logVertex4iv(const GLint *v)
+{
+ SIG( "glVertex4iv" );
+ dllVertex4iv( v );
+}
+static void APIENTRY logVertex4s(GLshort x, GLshort y, GLshort z, GLshort w)
+{
+ SIG( "glVertex4s" );
+ dllVertex4s( x, y, z, w );
+}
+static void APIENTRY logVertex4sv(const GLshort *v)
+{
+ SIG( "glVertex4sv" );
+ dllVertex4sv( v );
+}
+static void APIENTRY logVertexPointer(GLint size, GLenum type, GLsizei stride, const void *pointer)
+{
+ SIG( "glVertexPointer" );
+ dllVertexPointer( size, type, stride, pointer );
+}
+static void APIENTRY logViewport(GLint x, GLint y, GLsizei width, GLsizei height)
+{
+ SIG( "glViewport" );
+ dllViewport( x, y, width, height );
+}
+
+/*
+** QGL_Shutdown
+**
+** Unloads the specified DLL then nulls out all the proc pointers.
+*/
+void QGL_Shutdown( void )
+{
+ if ( glw_state.OpenGLLib )
+ {
+ dlclose ( glw_state.OpenGLLib );
+ glw_state.OpenGLLib = NULL;
+ }
+
+ glw_state.OpenGLLib = NULL;
+
+ qglAccum = NULL;
+ qglAlphaFunc = NULL;
+ qglAreTexturesResident = NULL;
+ qglArrayElement = NULL;
+ qglBegin = NULL;
+ qglBindTexture = NULL;
+ qglBitmap = NULL;
+ qglBlendFunc = NULL;
+ qglCallList = NULL;
+ qglCallLists = NULL;
+ qglClear = NULL;
+ qglClearAccum = NULL;
+ qglClearColor = NULL;
+ qglClearDepth = NULL;
+ qglClearIndex = NULL;
+ qglClearStencil = NULL;
+ qglClipPlane = NULL;
+ qglColor3b = NULL;
+ qglColor3bv = NULL;
+ qglColor3d = NULL;
+ qglColor3dv = NULL;
+ qglColor3f = NULL;
+ qglColor3fv = NULL;
+ qglColor3i = NULL;
+ qglColor3iv = NULL;
+ qglColor3s = NULL;
+ qglColor3sv = NULL;
+ qglColor3ub = NULL;
+ qglColor3ubv = NULL;
+ qglColor3ui = NULL;
+ qglColor3uiv = NULL;
+ qglColor3us = NULL;
+ qglColor3usv = NULL;
+ qglColor4b = NULL;
+ qglColor4bv = NULL;
+ qglColor4d = NULL;
+ qglColor4dv = NULL;
+ qglColor4f = NULL;
+ qglColor4fv = NULL;
+ qglColor4i = NULL;
+ qglColor4iv = NULL;
+ qglColor4s = NULL;
+ qglColor4sv = NULL;
+ qglColor4ub = NULL;
+ qglColor4ubv = NULL;
+ qglColor4ui = NULL;
+ qglColor4uiv = NULL;
+ qglColor4us = NULL;
+ qglColor4usv = NULL;
+ qglColorMask = NULL;
+ qglColorMaterial = NULL;
+ qglColorPointer = NULL;
+ qglCopyPixels = NULL;
+ qglCopyTexImage1D = NULL;
+ qglCopyTexImage2D = NULL;
+ qglCopyTexSubImage1D = NULL;
+ qglCopyTexSubImage2D = NULL;
+ qglCullFace = NULL;
+ qglDeleteLists = NULL;
+ qglDeleteTextures = NULL;
+ qglDepthFunc = NULL;
+ qglDepthMask = NULL;
+ qglDepthRange = NULL;
+ qglDisable = NULL;
+ qglDisableClientState = NULL;
+ qglDrawArrays = NULL;
+ qglDrawBuffer = NULL;
+ qglDrawElements = NULL;
+ qglDrawPixels = NULL;
+ qglEdgeFlag = NULL;
+ qglEdgeFlagPointer = NULL;
+ qglEdgeFlagv = NULL;
+ qglEnable = NULL;
+ qglEnableClientState = NULL;
+ qglEnd = NULL;
+ qglEndList = NULL;
+ qglEvalCoord1d = NULL;
+ qglEvalCoord1dv = NULL;
+ qglEvalCoord1f = NULL;
+ qglEvalCoord1fv = NULL;
+ qglEvalCoord2d = NULL;
+ qglEvalCoord2dv = NULL;
+ qglEvalCoord2f = NULL;
+ qglEvalCoord2fv = NULL;
+ qglEvalMesh1 = NULL;
+ qglEvalMesh2 = NULL;
+ qglEvalPoint1 = NULL;
+ qglEvalPoint2 = NULL;
+ qglFeedbackBuffer = NULL;
+ qglFinish = NULL;
+ qglFlush = NULL;
+ qglFogf = NULL;
+ qglFogfv = NULL;
+ qglFogi = NULL;
+ qglFogiv = NULL;
+ qglFrontFace = NULL;
+ qglFrustum = NULL;
+ qglGenLists = NULL;
+ qglGenTextures = NULL;
+ qglGetBooleanv = NULL;
+ qglGetClipPlane = NULL;
+ qglGetDoublev = NULL;
+ qglGetError = NULL;
+ qglGetFloatv = NULL;
+ qglGetIntegerv = NULL;
+ qglGetLightfv = NULL;
+ qglGetLightiv = NULL;
+ qglGetMapdv = NULL;
+ qglGetMapfv = NULL;
+ qglGetMapiv = NULL;
+ qglGetMaterialfv = NULL;
+ qglGetMaterialiv = NULL;
+ qglGetPixelMapfv = NULL;
+ qglGetPixelMapuiv = NULL;
+ qglGetPixelMapusv = NULL;
+ qglGetPointerv = NULL;
+ qglGetPolygonStipple = NULL;
+ qglGetString = NULL;
+ qglGetTexEnvfv = NULL;
+ qglGetTexEnviv = NULL;
+ qglGetTexGendv = NULL;
+ qglGetTexGenfv = NULL;
+ qglGetTexGeniv = NULL;
+ qglGetTexImage = NULL;
+ qglGetTexLevelParameterfv = NULL;
+ qglGetTexLevelParameteriv = NULL;
+ qglGetTexParameterfv = NULL;
+ qglGetTexParameteriv = NULL;
+ qglHint = NULL;
+ qglIndexMask = NULL;
+ qglIndexPointer = NULL;
+ qglIndexd = NULL;
+ qglIndexdv = NULL;
+ qglIndexf = NULL;
+ qglIndexfv = NULL;
+ qglIndexi = NULL;
+ qglIndexiv = NULL;
+ qglIndexs = NULL;
+ qglIndexsv = NULL;
+ qglIndexub = NULL;
+ qglIndexubv = NULL;
+ qglInitNames = NULL;
+ qglInterleavedArrays = NULL;
+ qglIsEnabled = NULL;
+ qglIsList = NULL;
+ qglIsTexture = NULL;
+ qglLightModelf = NULL;
+ qglLightModelfv = NULL;
+ qglLightModeli = NULL;
+ qglLightModeliv = NULL;
+ qglLightf = NULL;
+ qglLightfv = NULL;
+ qglLighti = NULL;
+ qglLightiv = NULL;
+ qglLineStipple = NULL;
+ qglLineWidth = NULL;
+ qglListBase = NULL;
+ qglLoadIdentity = NULL;
+ qglLoadMatrixd = NULL;
+ qglLoadMatrixf = NULL;
+ qglLoadName = NULL;
+ qglLogicOp = NULL;
+ qglMap1d = NULL;
+ qglMap1f = NULL;
+ qglMap2d = NULL;
+ qglMap2f = NULL;
+ qglMapGrid1d = NULL;
+ qglMapGrid1f = NULL;
+ qglMapGrid2d = NULL;
+ qglMapGrid2f = NULL;
+ qglMaterialf = NULL;
+ qglMaterialfv = NULL;
+ qglMateriali = NULL;
+ qglMaterialiv = NULL;
+ qglMatrixMode = NULL;
+ qglMultMatrixd = NULL;
+ qglMultMatrixf = NULL;
+ qglNewList = NULL;
+ qglNormal3b = NULL;
+ qglNormal3bv = NULL;
+ qglNormal3d = NULL;
+ qglNormal3dv = NULL;
+ qglNormal3f = NULL;
+ qglNormal3fv = NULL;
+ qglNormal3i = NULL;
+ qglNormal3iv = NULL;
+ qglNormal3s = NULL;
+ qglNormal3sv = NULL;
+ qglNormalPointer = NULL;
+ qglOrtho = NULL;
+ qglPassThrough = NULL;
+ qglPixelMapfv = NULL;
+ qglPixelMapuiv = NULL;
+ qglPixelMapusv = NULL;
+ qglPixelStoref = NULL;
+ qglPixelStorei = NULL;
+ qglPixelTransferf = NULL;
+ qglPixelTransferi = NULL;
+ qglPixelZoom = NULL;
+ qglPointSize = NULL;
+ qglPolygonMode = NULL;
+ qglPolygonOffset = NULL;
+ qglPolygonStipple = NULL;
+ qglPopAttrib = NULL;
+ qglPopClientAttrib = NULL;
+ qglPopMatrix = NULL;
+ qglPopName = NULL;
+ qglPrioritizeTextures = NULL;
+ qglPushAttrib = NULL;
+ qglPushClientAttrib = NULL;
+ qglPushMatrix = NULL;
+ qglPushName = NULL;
+ qglRasterPos2d = NULL;
+ qglRasterPos2dv = NULL;
+ qglRasterPos2f = NULL;
+ qglRasterPos2fv = NULL;
+ qglRasterPos2i = NULL;
+ qglRasterPos2iv = NULL;
+ qglRasterPos2s = NULL;
+ qglRasterPos2sv = NULL;
+ qglRasterPos3d = NULL;
+ qglRasterPos3dv = NULL;
+ qglRasterPos3f = NULL;
+ qglRasterPos3fv = NULL;
+ qglRasterPos3i = NULL;
+ qglRasterPos3iv = NULL;
+ qglRasterPos3s = NULL;
+ qglRasterPos3sv = NULL;
+ qglRasterPos4d = NULL;
+ qglRasterPos4dv = NULL;
+ qglRasterPos4f = NULL;
+ qglRasterPos4fv = NULL;
+ qglRasterPos4i = NULL;
+ qglRasterPos4iv = NULL;
+ qglRasterPos4s = NULL;
+ qglRasterPos4sv = NULL;
+ qglReadBuffer = NULL;
+ qglReadPixels = NULL;
+ qglRectd = NULL;
+ qglRectdv = NULL;
+ qglRectf = NULL;
+ qglRectfv = NULL;
+ qglRecti = NULL;
+ qglRectiv = NULL;
+ qglRects = NULL;
+ qglRectsv = NULL;
+ qglRenderMode = NULL;
+ qglRotated = NULL;
+ qglRotatef = NULL;
+ qglScaled = NULL;
+ qglScalef = NULL;
+ qglScissor = NULL;
+ qglSelectBuffer = NULL;
+ qglShadeModel = NULL;
+ qglStencilFunc = NULL;
+ qglStencilMask = NULL;
+ qglStencilOp = NULL;
+ qglTexCoord1d = NULL;
+ qglTexCoord1dv = NULL;
+ qglTexCoord1f = NULL;
+ qglTexCoord1fv = NULL;
+ qglTexCoord1i = NULL;
+ qglTexCoord1iv = NULL;
+ qglTexCoord1s = NULL;
+ qglTexCoord1sv = NULL;
+ qglTexCoord2d = NULL;
+ qglTexCoord2dv = NULL;
+ qglTexCoord2f = NULL;
+ qglTexCoord2fv = NULL;
+ qglTexCoord2i = NULL;
+ qglTexCoord2iv = NULL;
+ qglTexCoord2s = NULL;
+ qglTexCoord2sv = NULL;
+ qglTexCoord3d = NULL;
+ qglTexCoord3dv = NULL;
+ qglTexCoord3f = NULL;
+ qglTexCoord3fv = NULL;
+ qglTexCoord3i = NULL;
+ qglTexCoord3iv = NULL;
+ qglTexCoord3s = NULL;
+ qglTexCoord3sv = NULL;
+ qglTexCoord4d = NULL;
+ qglTexCoord4dv = NULL;
+ qglTexCoord4f = NULL;
+ qglTexCoord4fv = NULL;
+ qglTexCoord4i = NULL;
+ qglTexCoord4iv = NULL;
+ qglTexCoord4s = NULL;
+ qglTexCoord4sv = NULL;
+ qglTexCoordPointer = NULL;
+ qglTexEnvf = NULL;
+ qglTexEnvfv = NULL;
+ qglTexEnvi = NULL;
+ qglTexEnviv = NULL;
+ qglTexGend = NULL;
+ qglTexGendv = NULL;
+ qglTexGenf = NULL;
+ qglTexGenfv = NULL;
+ qglTexGeni = NULL;
+ qglTexGeniv = NULL;
+ qglTexImage1D = NULL;
+ qglTexImage2D = NULL;
+ qglTexParameterf = NULL;
+ qglTexParameterfv = NULL;
+ qglTexParameteri = NULL;
+ qglTexParameteriv = NULL;
+ qglTexSubImage1D = NULL;
+ qglTexSubImage2D = NULL;
+ qglTranslated = NULL;
+ qglTranslatef = NULL;
+ qglVertex2d = NULL;
+ qglVertex2dv = NULL;
+ qglVertex2f = NULL;
+ qglVertex2fv = NULL;
+ qglVertex2i = NULL;
+ qglVertex2iv = NULL;
+ qglVertex2s = NULL;
+ qglVertex2sv = NULL;
+ qglVertex3d = NULL;
+ qglVertex3dv = NULL;
+ qglVertex3f = NULL;
+ qglVertex3fv = NULL;
+ qglVertex3i = NULL;
+ qglVertex3iv = NULL;
+ qglVertex3s = NULL;
+ qglVertex3sv = NULL;
+ qglVertex4d = NULL;
+ qglVertex4dv = NULL;
+ qglVertex4f = NULL;
+ qglVertex4fv = NULL;
+ qglVertex4i = NULL;
+ qglVertex4iv = NULL;
+ qglVertex4s = NULL;
+ qglVertex4sv = NULL;
+ qglVertexPointer = NULL;
+ qglViewport = NULL;
+
+// bk001129 - from cvs1.17 (mkv)
+#if defined(__FX__)
+ qfxMesaCreateContext = NULL;
+ qfxMesaCreateBestContext = NULL;
+ qfxMesaDestroyContext = NULL;
+ qfxMesaMakeCurrent = NULL;
+ qfxMesaGetCurrentContext = NULL;
+ qfxMesaSwapBuffers = NULL;
+#endif
+
+ qglXChooseVisual = NULL;
+ qglXCreateContext = NULL;
+ qglXDestroyContext = NULL;
+ qglXMakeCurrent = NULL;
+ qglXCopyContext = NULL;
+ qglXSwapBuffers = NULL;
+}
+
+#define GPA( a ) dlsym( glw_state.OpenGLLib, a )
+
+void *qwglGetProcAddress(char *symbol)
+{
+ if (glw_state.OpenGLLib)
+ return GPA ( symbol );
+ return NULL;
+}
+
+/*
+** QGL_Init
+**
+** This is responsible for binding our qgl function pointers to
+** the appropriate GL stuff. In Windows this means doing a
+** LoadLibrary and a bunch of calls to GetProcAddress. On other
+** operating systems we need to do the right thing, whatever that
+** might be.
+**
+*/
+
+qboolean QGL_Init( const char *dllname )
+{
+ if ( ( glw_state.OpenGLLib = dlopen( dllname, RTLD_LAZY|RTLD_GLOBAL ) ) == 0 )
+ {
+ char fn[1024];
+ // FILE *fp; // bk001204 - unused
+ extern uid_t saved_euid; // unix_main.c
+
+ // if we are not setuid, try current directory
+ if (getuid() == saved_euid) {
+ getcwd(fn, sizeof(fn));
+ Q_strcat(fn, sizeof(fn), "/");
+ Q_strcat(fn, sizeof(fn), dllname);
+
+ if ( ( glw_state.OpenGLLib = dlopen( fn, RTLD_LAZY ) ) == 0 ) {
+ ri.Printf(PRINT_ALL, "QGL_Init: Can't load %s from /etc/ld.so.conf or current dir: %s\n", dllname, dlerror());
+ return qfalse;
+ }
+ } else {
+ ri.Printf(PRINT_ALL, "QGL_Init: Can't load %s from /etc/ld.so.conf: %s\n", dllname, dlerror());
+ return qfalse;
+ }
+ }
+
+ qglAccum = dllAccum = GPA( "glAccum" );
+ qglAlphaFunc = dllAlphaFunc = GPA( "glAlphaFunc" );
+ qglAreTexturesResident = dllAreTexturesResident = GPA( "glAreTexturesResident" );
+ qglArrayElement = dllArrayElement = GPA( "glArrayElement" );
+ qglBegin = dllBegin = GPA( "glBegin" );
+ qglBindTexture = dllBindTexture = GPA( "glBindTexture" );
+ qglBitmap = dllBitmap = GPA( "glBitmap" );
+ qglBlendFunc = dllBlendFunc = GPA( "glBlendFunc" );
+ qglCallList = dllCallList = GPA( "glCallList" );
+ qglCallLists = dllCallLists = GPA( "glCallLists" );
+ qglClear = dllClear = GPA( "glClear" );
+ qglClearAccum = dllClearAccum = GPA( "glClearAccum" );
+ qglClearColor = dllClearColor = GPA( "glClearColor" );
+ qglClearDepth = dllClearDepth = GPA( "glClearDepth" );
+ qglClearIndex = dllClearIndex = GPA( "glClearIndex" );
+ qglClearStencil = dllClearStencil = GPA( "glClearStencil" );
+ qglClipPlane = dllClipPlane = GPA( "glClipPlane" );
+ qglColor3b = dllColor3b = GPA( "glColor3b" );
+ qglColor3bv = dllColor3bv = GPA( "glColor3bv" );
+ qglColor3d = dllColor3d = GPA( "glColor3d" );
+ qglColor3dv = dllColor3dv = GPA( "glColor3dv" );
+ qglColor3f = dllColor3f = GPA( "glColor3f" );
+ qglColor3fv = dllColor3fv = GPA( "glColor3fv" );
+ qglColor3i = dllColor3i = GPA( "glColor3i" );
+ qglColor3iv = dllColor3iv = GPA( "glColor3iv" );
+ qglColor3s = dllColor3s = GPA( "glColor3s" );
+ qglColor3sv = dllColor3sv = GPA( "glColor3sv" );
+ qglColor3ub = dllColor3ub = GPA( "glColor3ub" );
+ qglColor3ubv = dllColor3ubv = GPA( "glColor3ubv" );
+ qglColor3ui = dllColor3ui = GPA( "glColor3ui" );
+ qglColor3uiv = dllColor3uiv = GPA( "glColor3uiv" );
+ qglColor3us = dllColor3us = GPA( "glColor3us" );
+ qglColor3usv = dllColor3usv = GPA( "glColor3usv" );
+ qglColor4b = dllColor4b = GPA( "glColor4b" );
+ qglColor4bv = dllColor4bv = GPA( "glColor4bv" );
+ qglColor4d = dllColor4d = GPA( "glColor4d" );
+ qglColor4dv = dllColor4dv = GPA( "glColor4dv" );
+ qglColor4f = dllColor4f = GPA( "glColor4f" );
+ qglColor4fv = dllColor4fv = GPA( "glColor4fv" );
+ qglColor4i = dllColor4i = GPA( "glColor4i" );
+ qglColor4iv = dllColor4iv = GPA( "glColor4iv" );
+ qglColor4s = dllColor4s = GPA( "glColor4s" );
+ qglColor4sv = dllColor4sv = GPA( "glColor4sv" );
+ qglColor4ub = dllColor4ub = GPA( "glColor4ub" );
+ qglColor4ubv = dllColor4ubv = GPA( "glColor4ubv" );
+ qglColor4ui = dllColor4ui = GPA( "glColor4ui" );
+ qglColor4uiv = dllColor4uiv = GPA( "glColor4uiv" );
+ qglColor4us = dllColor4us = GPA( "glColor4us" );
+ qglColor4usv = dllColor4usv = GPA( "glColor4usv" );
+ qglColorMask = dllColorMask = GPA( "glColorMask" );
+ qglColorMaterial = dllColorMaterial = GPA( "glColorMaterial" );
+ qglColorPointer = dllColorPointer = GPA( "glColorPointer" );
+ qglCopyPixels = dllCopyPixels = GPA( "glCopyPixels" );
+ qglCopyTexImage1D = dllCopyTexImage1D = GPA( "glCopyTexImage1D" );
+ qglCopyTexImage2D = dllCopyTexImage2D = GPA( "glCopyTexImage2D" );
+ qglCopyTexSubImage1D = dllCopyTexSubImage1D = GPA( "glCopyTexSubImage1D" );
+ qglCopyTexSubImage2D = dllCopyTexSubImage2D = GPA( "glCopyTexSubImage2D" );
+ qglCullFace = dllCullFace = GPA( "glCullFace" );
+ qglDeleteLists = dllDeleteLists = GPA( "glDeleteLists" );
+ qglDeleteTextures = dllDeleteTextures = GPA( "glDeleteTextures" );
+ qglDepthFunc = dllDepthFunc = GPA( "glDepthFunc" );
+ qglDepthMask = dllDepthMask = GPA( "glDepthMask" );
+ qglDepthRange = dllDepthRange = GPA( "glDepthRange" );
+ qglDisable = dllDisable = GPA( "glDisable" );
+ qglDisableClientState = dllDisableClientState = GPA( "glDisableClientState" );
+ qglDrawArrays = dllDrawArrays = GPA( "glDrawArrays" );
+ qglDrawBuffer = dllDrawBuffer = GPA( "glDrawBuffer" );
+ qglDrawElements = dllDrawElements = GPA( "glDrawElements" );
+ qglDrawPixels = dllDrawPixels = GPA( "glDrawPixels" );
+ qglEdgeFlag = dllEdgeFlag = GPA( "glEdgeFlag" );
+ qglEdgeFlagPointer = dllEdgeFlagPointer = GPA( "glEdgeFlagPointer" );
+ qglEdgeFlagv = dllEdgeFlagv = GPA( "glEdgeFlagv" );
+ qglEnable = dllEnable = GPA( "glEnable" );
+ qglEnableClientState = dllEnableClientState = GPA( "glEnableClientState" );
+ qglEnd = dllEnd = GPA( "glEnd" );
+ qglEndList = dllEndList = GPA( "glEndList" );
+ qglEvalCoord1d = dllEvalCoord1d = GPA( "glEvalCoord1d" );
+ qglEvalCoord1dv = dllEvalCoord1dv = GPA( "glEvalCoord1dv" );
+ qglEvalCoord1f = dllEvalCoord1f = GPA( "glEvalCoord1f" );
+ qglEvalCoord1fv = dllEvalCoord1fv = GPA( "glEvalCoord1fv" );
+ qglEvalCoord2d = dllEvalCoord2d = GPA( "glEvalCoord2d" );
+ qglEvalCoord2dv = dllEvalCoord2dv = GPA( "glEvalCoord2dv" );
+ qglEvalCoord2f = dllEvalCoord2f = GPA( "glEvalCoord2f" );
+ qglEvalCoord2fv = dllEvalCoord2fv = GPA( "glEvalCoord2fv" );
+ qglEvalMesh1 = dllEvalMesh1 = GPA( "glEvalMesh1" );
+ qglEvalMesh2 = dllEvalMesh2 = GPA( "glEvalMesh2" );
+ qglEvalPoint1 = dllEvalPoint1 = GPA( "glEvalPoint1" );
+ qglEvalPoint2 = dllEvalPoint2 = GPA( "glEvalPoint2" );
+ qglFeedbackBuffer = dllFeedbackBuffer = GPA( "glFeedbackBuffer" );
+ qglFinish = dllFinish = GPA( "glFinish" );
+ qglFlush = dllFlush = GPA( "glFlush" );
+ qglFogf = dllFogf = GPA( "glFogf" );
+ qglFogfv = dllFogfv = GPA( "glFogfv" );
+ qglFogi = dllFogi = GPA( "glFogi" );
+ qglFogiv = dllFogiv = GPA( "glFogiv" );
+ qglFrontFace = dllFrontFace = GPA( "glFrontFace" );
+ qglFrustum = dllFrustum = GPA( "glFrustum" );
+ qglGenLists = dllGenLists = GPA( "glGenLists" );
+ qglGenTextures = dllGenTextures = GPA( "glGenTextures" );
+ qglGetBooleanv = dllGetBooleanv = GPA( "glGetBooleanv" );
+ qglGetClipPlane = dllGetClipPlane = GPA( "glGetClipPlane" );
+ qglGetDoublev = dllGetDoublev = GPA( "glGetDoublev" );
+ qglGetError = dllGetError = GPA( "glGetError" );
+ qglGetFloatv = dllGetFloatv = GPA( "glGetFloatv" );
+ qglGetIntegerv = dllGetIntegerv = GPA( "glGetIntegerv" );
+ qglGetLightfv = dllGetLightfv = GPA( "glGetLightfv" );
+ qglGetLightiv = dllGetLightiv = GPA( "glGetLightiv" );
+ qglGetMapdv = dllGetMapdv = GPA( "glGetMapdv" );
+ qglGetMapfv = dllGetMapfv = GPA( "glGetMapfv" );
+ qglGetMapiv = dllGetMapiv = GPA( "glGetMapiv" );
+ qglGetMaterialfv = dllGetMaterialfv = GPA( "glGetMaterialfv" );
+ qglGetMaterialiv = dllGetMaterialiv = GPA( "glGetMaterialiv" );
+ qglGetPixelMapfv = dllGetPixelMapfv = GPA( "glGetPixelMapfv" );
+ qglGetPixelMapuiv = dllGetPixelMapuiv = GPA( "glGetPixelMapuiv" );
+ qglGetPixelMapusv = dllGetPixelMapusv = GPA( "glGetPixelMapusv" );
+ qglGetPointerv = dllGetPointerv = GPA( "glGetPointerv" );
+ qglGetPolygonStipple = dllGetPolygonStipple = GPA( "glGetPolygonStipple" );
+ qglGetString = dllGetString = GPA( "glGetString" );
+ qglGetTexEnvfv = dllGetTexEnvfv = GPA( "glGetTexEnvfv" );
+ qglGetTexEnviv = dllGetTexEnviv = GPA( "glGetTexEnviv" );
+ qglGetTexGendv = dllGetTexGendv = GPA( "glGetTexGendv" );
+ qglGetTexGenfv = dllGetTexGenfv = GPA( "glGetTexGenfv" );
+ qglGetTexGeniv = dllGetTexGeniv = GPA( "glGetTexGeniv" );
+ qglGetTexImage = dllGetTexImage = GPA( "glGetTexImage" );
+ qglGetTexParameterfv = dllGetTexParameterfv = GPA( "glGetTexParameterfv" );
+ qglGetTexParameteriv = dllGetTexParameteriv = GPA( "glGetTexParameteriv" );
+ qglHint = dllHint = GPA( "glHint" );
+ qglIndexMask = dllIndexMask = GPA( "glIndexMask" );
+ qglIndexPointer = dllIndexPointer = GPA( "glIndexPointer" );
+ qglIndexd = dllIndexd = GPA( "glIndexd" );
+ qglIndexdv = dllIndexdv = GPA( "glIndexdv" );
+ qglIndexf = dllIndexf = GPA( "glIndexf" );
+ qglIndexfv = dllIndexfv = GPA( "glIndexfv" );
+ qglIndexi = dllIndexi = GPA( "glIndexi" );
+ qglIndexiv = dllIndexiv = GPA( "glIndexiv" );
+ qglIndexs = dllIndexs = GPA( "glIndexs" );
+ qglIndexsv = dllIndexsv = GPA( "glIndexsv" );
+ qglIndexub = dllIndexub = GPA( "glIndexub" );
+ qglIndexubv = dllIndexubv = GPA( "glIndexubv" );
+ qglInitNames = dllInitNames = GPA( "glInitNames" );
+ qglInterleavedArrays = dllInterleavedArrays = GPA( "glInterleavedArrays" );
+ qglIsEnabled = dllIsEnabled = GPA( "glIsEnabled" );
+ qglIsList = dllIsList = GPA( "glIsList" );
+ qglIsTexture = dllIsTexture = GPA( "glIsTexture" );
+ qglLightModelf = dllLightModelf = GPA( "glLightModelf" );
+ qglLightModelfv = dllLightModelfv = GPA( "glLightModelfv" );
+ qglLightModeli = dllLightModeli = GPA( "glLightModeli" );
+ qglLightModeliv = dllLightModeliv = GPA( "glLightModeliv" );
+ qglLightf = dllLightf = GPA( "glLightf" );
+ qglLightfv = dllLightfv = GPA( "glLightfv" );
+ qglLighti = dllLighti = GPA( "glLighti" );
+ qglLightiv = dllLightiv = GPA( "glLightiv" );
+ qglLineStipple = dllLineStipple = GPA( "glLineStipple" );
+ qglLineWidth = dllLineWidth = GPA( "glLineWidth" );
+ qglListBase = dllListBase = GPA( "glListBase" );
+ qglLoadIdentity = dllLoadIdentity = GPA( "glLoadIdentity" );
+ qglLoadMatrixd = dllLoadMatrixd = GPA( "glLoadMatrixd" );
+ qglLoadMatrixf = dllLoadMatrixf = GPA( "glLoadMatrixf" );
+ qglLoadName = dllLoadName = GPA( "glLoadName" );
+ qglLogicOp = dllLogicOp = GPA( "glLogicOp" );
+ qglMap1d = dllMap1d = GPA( "glMap1d" );
+ qglMap1f = dllMap1f = GPA( "glMap1f" );
+ qglMap2d = dllMap2d = GPA( "glMap2d" );
+ qglMap2f = dllMap2f = GPA( "glMap2f" );
+ qglMapGrid1d = dllMapGrid1d = GPA( "glMapGrid1d" );
+ qglMapGrid1f = dllMapGrid1f = GPA( "glMapGrid1f" );
+ qglMapGrid2d = dllMapGrid2d = GPA( "glMapGrid2d" );
+ qglMapGrid2f = dllMapGrid2f = GPA( "glMapGrid2f" );
+ qglMaterialf = dllMaterialf = GPA( "glMaterialf" );
+ qglMaterialfv = dllMaterialfv = GPA( "glMaterialfv" );
+ qglMateriali = dllMateriali = GPA( "glMateriali" );
+ qglMaterialiv = dllMaterialiv = GPA( "glMaterialiv" );
+ qglMatrixMode = dllMatrixMode = GPA( "glMatrixMode" );
+ qglMultMatrixd = dllMultMatrixd = GPA( "glMultMatrixd" );
+ qglMultMatrixf = dllMultMatrixf = GPA( "glMultMatrixf" );
+ qglNewList = dllNewList = GPA( "glNewList" );
+ qglNormal3b = dllNormal3b = GPA( "glNormal3b" );
+ qglNormal3bv = dllNormal3bv = GPA( "glNormal3bv" );
+ qglNormal3d = dllNormal3d = GPA( "glNormal3d" );
+ qglNormal3dv = dllNormal3dv = GPA( "glNormal3dv" );
+ qglNormal3f = dllNormal3f = GPA( "glNormal3f" );
+ qglNormal3fv = dllNormal3fv = GPA( "glNormal3fv" );
+ qglNormal3i = dllNormal3i = GPA( "glNormal3i" );
+ qglNormal3iv = dllNormal3iv = GPA( "glNormal3iv" );
+ qglNormal3s = dllNormal3s = GPA( "glNormal3s" );
+ qglNormal3sv = dllNormal3sv = GPA( "glNormal3sv" );
+ qglNormalPointer = dllNormalPointer = GPA( "glNormalPointer" );
+ qglOrtho = dllOrtho = GPA( "glOrtho" );
+ qglPassThrough = dllPassThrough = GPA( "glPassThrough" );
+ qglPixelMapfv = dllPixelMapfv = GPA( "glPixelMapfv" );
+ qglPixelMapuiv = dllPixelMapuiv = GPA( "glPixelMapuiv" );
+ qglPixelMapusv = dllPixelMapusv = GPA( "glPixelMapusv" );
+ qglPixelStoref = dllPixelStoref = GPA( "glPixelStoref" );
+ qglPixelStorei = dllPixelStorei = GPA( "glPixelStorei" );
+ qglPixelTransferf = dllPixelTransferf = GPA( "glPixelTransferf" );
+ qglPixelTransferi = dllPixelTransferi = GPA( "glPixelTransferi" );
+ qglPixelZoom = dllPixelZoom = GPA( "glPixelZoom" );
+ qglPointSize = dllPointSize = GPA( "glPointSize" );
+ qglPolygonMode = dllPolygonMode = GPA( "glPolygonMode" );
+ qglPolygonOffset = dllPolygonOffset = GPA( "glPolygonOffset" );
+ qglPolygonStipple = dllPolygonStipple = GPA( "glPolygonStipple" );
+ qglPopAttrib = dllPopAttrib = GPA( "glPopAttrib" );
+ qglPopClientAttrib = dllPopClientAttrib = GPA( "glPopClientAttrib" );
+ qglPopMatrix = dllPopMatrix = GPA( "glPopMatrix" );
+ qglPopName = dllPopName = GPA( "glPopName" );
+ qglPrioritizeTextures = dllPrioritizeTextures = GPA( "glPrioritizeTextures" );
+ qglPushAttrib = dllPushAttrib = GPA( "glPushAttrib" );
+ qglPushClientAttrib = dllPushClientAttrib = GPA( "glPushClientAttrib" );
+ qglPushMatrix = dllPushMatrix = GPA( "glPushMatrix" );
+ qglPushName = dllPushName = GPA( "glPushName" );
+ qglRasterPos2d = dllRasterPos2d = GPA( "glRasterPos2d" );
+ qglRasterPos2dv = dllRasterPos2dv = GPA( "glRasterPos2dv" );
+ qglRasterPos2f = dllRasterPos2f = GPA( "glRasterPos2f" );
+ qglRasterPos2fv = dllRasterPos2fv = GPA( "glRasterPos2fv" );
+ qglRasterPos2i = dllRasterPos2i = GPA( "glRasterPos2i" );
+ qglRasterPos2iv = dllRasterPos2iv = GPA( "glRasterPos2iv" );
+ qglRasterPos2s = dllRasterPos2s = GPA( "glRasterPos2s" );
+ qglRasterPos2sv = dllRasterPos2sv = GPA( "glRasterPos2sv" );
+ qglRasterPos3d = dllRasterPos3d = GPA( "glRasterPos3d" );
+ qglRasterPos3dv = dllRasterPos3dv = GPA( "glRasterPos3dv" );
+ qglRasterPos3f = dllRasterPos3f = GPA( "glRasterPos3f" );
+ qglRasterPos3fv = dllRasterPos3fv = GPA( "glRasterPos3fv" );
+ qglRasterPos3i = dllRasterPos3i = GPA( "glRasterPos3i" );
+ qglRasterPos3iv = dllRasterPos3iv = GPA( "glRasterPos3iv" );
+ qglRasterPos3s = dllRasterPos3s = GPA( "glRasterPos3s" );
+ qglRasterPos3sv = dllRasterPos3sv = GPA( "glRasterPos3sv" );
+ qglRasterPos4d = dllRasterPos4d = GPA( "glRasterPos4d" );
+ qglRasterPos4dv = dllRasterPos4dv = GPA( "glRasterPos4dv" );
+ qglRasterPos4f = dllRasterPos4f = GPA( "glRasterPos4f" );
+ qglRasterPos4fv = dllRasterPos4fv = GPA( "glRasterPos4fv" );
+ qglRasterPos4i = dllRasterPos4i = GPA( "glRasterPos4i" );
+ qglRasterPos4iv = dllRasterPos4iv = GPA( "glRasterPos4iv" );
+ qglRasterPos4s = dllRasterPos4s = GPA( "glRasterPos4s" );
+ qglRasterPos4sv = dllRasterPos4sv = GPA( "glRasterPos4sv" );
+ qglReadBuffer = dllReadBuffer = GPA( "glReadBuffer" );
+ qglReadPixels = dllReadPixels = GPA( "glReadPixels" );
+ qglRectd = dllRectd = GPA( "glRectd" );
+ qglRectdv = dllRectdv = GPA( "glRectdv" );
+ qglRectf = dllRectf = GPA( "glRectf" );
+ qglRectfv = dllRectfv = GPA( "glRectfv" );
+ qglRecti = dllRecti = GPA( "glRecti" );
+ qglRectiv = dllRectiv = GPA( "glRectiv" );
+ qglRects = dllRects = GPA( "glRects" );
+ qglRectsv = dllRectsv = GPA( "glRectsv" );
+ qglRenderMode = dllRenderMode = GPA( "glRenderMode" );
+ qglRotated = dllRotated = GPA( "glRotated" );
+ qglRotatef = dllRotatef = GPA( "glRotatef" );
+ qglScaled = dllScaled = GPA( "glScaled" );
+ qglScalef = dllScalef = GPA( "glScalef" );
+ qglScissor = dllScissor = GPA( "glScissor" );
+ qglSelectBuffer = dllSelectBuffer = GPA( "glSelectBuffer" );
+ qglShadeModel = dllShadeModel = GPA( "glShadeModel" );
+ qglStencilFunc = dllStencilFunc = GPA( "glStencilFunc" );
+ qglStencilMask = dllStencilMask = GPA( "glStencilMask" );
+ qglStencilOp = dllStencilOp = GPA( "glStencilOp" );
+ qglTexCoord1d = dllTexCoord1d = GPA( "glTexCoord1d" );
+ qglTexCoord1dv = dllTexCoord1dv = GPA( "glTexCoord1dv" );
+ qglTexCoord1f = dllTexCoord1f = GPA( "glTexCoord1f" );
+ qglTexCoord1fv = dllTexCoord1fv = GPA( "glTexCoord1fv" );
+ qglTexCoord1i = dllTexCoord1i = GPA( "glTexCoord1i" );
+ qglTexCoord1iv = dllTexCoord1iv = GPA( "glTexCoord1iv" );
+ qglTexCoord1s = dllTexCoord1s = GPA( "glTexCoord1s" );
+ qglTexCoord1sv = dllTexCoord1sv = GPA( "glTexCoord1sv" );
+ qglTexCoord2d = dllTexCoord2d = GPA( "glTexCoord2d" );
+ qglTexCoord2dv = dllTexCoord2dv = GPA( "glTexCoord2dv" );
+ qglTexCoord2f = dllTexCoord2f = GPA( "glTexCoord2f" );
+ qglTexCoord2fv = dllTexCoord2fv = GPA( "glTexCoord2fv" );
+ qglTexCoord2i = dllTexCoord2i = GPA( "glTexCoord2i" );
+ qglTexCoord2iv = dllTexCoord2iv = GPA( "glTexCoord2iv" );
+ qglTexCoord2s = dllTexCoord2s = GPA( "glTexCoord2s" );
+ qglTexCoord2sv = dllTexCoord2sv = GPA( "glTexCoord2sv" );
+ qglTexCoord3d = dllTexCoord3d = GPA( "glTexCoord3d" );
+ qglTexCoord3dv = dllTexCoord3dv = GPA( "glTexCoord3dv" );
+ qglTexCoord3f = dllTexCoord3f = GPA( "glTexCoord3f" );
+ qglTexCoord3fv = dllTexCoord3fv = GPA( "glTexCoord3fv" );
+ qglTexCoord3i = dllTexCoord3i = GPA( "glTexCoord3i" );
+ qglTexCoord3iv = dllTexCoord3iv = GPA( "glTexCoord3iv" );
+ qglTexCoord3s = dllTexCoord3s = GPA( "glTexCoord3s" );
+ qglTexCoord3sv = dllTexCoord3sv = GPA( "glTexCoord3sv" );
+ qglTexCoord4d = dllTexCoord4d = GPA( "glTexCoord4d" );
+ qglTexCoord4dv = dllTexCoord4dv = GPA( "glTexCoord4dv" );
+ qglTexCoord4f = dllTexCoord4f = GPA( "glTexCoord4f" );
+ qglTexCoord4fv = dllTexCoord4fv = GPA( "glTexCoord4fv" );
+ qglTexCoord4i = dllTexCoord4i = GPA( "glTexCoord4i" );
+ qglTexCoord4iv = dllTexCoord4iv = GPA( "glTexCoord4iv" );
+ qglTexCoord4s = dllTexCoord4s = GPA( "glTexCoord4s" );
+ qglTexCoord4sv = dllTexCoord4sv = GPA( "glTexCoord4sv" );
+ qglTexCoordPointer = dllTexCoordPointer = GPA( "glTexCoordPointer" );
+ qglTexEnvf = dllTexEnvf = GPA( "glTexEnvf" );
+ qglTexEnvfv = dllTexEnvfv = GPA( "glTexEnvfv" );
+ qglTexEnvi = dllTexEnvi = GPA( "glTexEnvi" );
+ qglTexEnviv = dllTexEnviv = GPA( "glTexEnviv" );
+ qglTexGend = dllTexGend = GPA( "glTexGend" );
+ qglTexGendv = dllTexGendv = GPA( "glTexGendv" );
+ qglTexGenf = dllTexGenf = GPA( "glTexGenf" );
+ qglTexGenfv = dllTexGenfv = GPA( "glTexGenfv" );
+ qglTexGeni = dllTexGeni = GPA( "glTexGeni" );
+ qglTexGeniv = dllTexGeniv = GPA( "glTexGeniv" );
+ qglTexImage1D = dllTexImage1D = GPA( "glTexImage1D" );
+ qglTexImage2D = dllTexImage2D = GPA( "glTexImage2D" );
+ qglTexParameterf = dllTexParameterf = GPA( "glTexParameterf" );
+ qglTexParameterfv = dllTexParameterfv = GPA( "glTexParameterfv" );
+ qglTexParameteri = dllTexParameteri = GPA( "glTexParameteri" );
+ qglTexParameteriv = dllTexParameteriv = GPA( "glTexParameteriv" );
+ qglTexSubImage1D = dllTexSubImage1D = GPA( "glTexSubImage1D" );
+ qglTexSubImage2D = dllTexSubImage2D = GPA( "glTexSubImage2D" );
+ qglTranslated = dllTranslated = GPA( "glTranslated" );
+ qglTranslatef = dllTranslatef = GPA( "glTranslatef" );
+ qglVertex2d = dllVertex2d = GPA( "glVertex2d" );
+ qglVertex2dv = dllVertex2dv = GPA( "glVertex2dv" );
+ qglVertex2f = dllVertex2f = GPA( "glVertex2f" );
+ qglVertex2fv = dllVertex2fv = GPA( "glVertex2fv" );
+ qglVertex2i = dllVertex2i = GPA( "glVertex2i" );
+ qglVertex2iv = dllVertex2iv = GPA( "glVertex2iv" );
+ qglVertex2s = dllVertex2s = GPA( "glVertex2s" );
+ qglVertex2sv = dllVertex2sv = GPA( "glVertex2sv" );
+ qglVertex3d = dllVertex3d = GPA( "glVertex3d" );
+ qglVertex3dv = dllVertex3dv = GPA( "glVertex3dv" );
+ qglVertex3f = dllVertex3f = GPA( "glVertex3f" );
+ qglVertex3fv = dllVertex3fv = GPA( "glVertex3fv" );
+ qglVertex3i = dllVertex3i = GPA( "glVertex3i" );
+ qglVertex3iv = dllVertex3iv = GPA( "glVertex3iv" );
+ qglVertex3s = dllVertex3s = GPA( "glVertex3s" );
+ qglVertex3sv = dllVertex3sv = GPA( "glVertex3sv" );
+ qglVertex4d = dllVertex4d = GPA( "glVertex4d" );
+ qglVertex4dv = dllVertex4dv = GPA( "glVertex4dv" );
+ qglVertex4f = dllVertex4f = GPA( "glVertex4f" );
+ qglVertex4fv = dllVertex4fv = GPA( "glVertex4fv" );
+ qglVertex4i = dllVertex4i = GPA( "glVertex4i" );
+ qglVertex4iv = dllVertex4iv = GPA( "glVertex4iv" );
+ qglVertex4s = dllVertex4s = GPA( "glVertex4s" );
+ qglVertex4sv = dllVertex4sv = GPA( "glVertex4sv" );
+ qglVertexPointer = dllVertexPointer = GPA( "glVertexPointer" );
+ qglViewport = dllViewport = GPA( "glViewport" );
+
+// bk001129 - from cvs1.17 (mkv)
+#if defined(__FX__)
+ qfxMesaCreateContext = GPA("fxMesaCreateContext");
+ qfxMesaCreateBestContext = GPA("fxMesaCreateBestContext");
+ qfxMesaDestroyContext = GPA("fxMesaDestroyContext");
+ qfxMesaMakeCurrent = GPA("fxMesaMakeCurrent");
+ qfxMesaGetCurrentContext = GPA("fxMesaGetCurrentContext");
+ qfxMesaSwapBuffers = GPA("fxMesaSwapBuffers");
+#endif
+
+ qglXChooseVisual = GPA("glXChooseVisual");
+ qglXCreateContext = GPA("glXCreateContext");
+ qglXDestroyContext = GPA("glXDestroyContext");
+ qglXMakeCurrent = GPA("glXMakeCurrent");
+ qglXCopyContext = GPA("glXCopyContext");
+ qglXSwapBuffers = GPA("glXSwapBuffers");
+
+ qglLockArraysEXT = 0;
+ qglUnlockArraysEXT = 0;
+ qglPointParameterfEXT = 0;
+ qglPointParameterfvEXT = 0;
+ qglColorTableEXT = 0;
+ qgl3DfxSetPaletteEXT = 0;
+ qglSelectTextureSGIS = 0;
+ qglMTexCoord2fSGIS = 0;
+ qglActiveTextureARB = 0;
+ qglClientActiveTextureARB = 0;
+ qglMultiTexCoord2fARB = 0;
+
+ return qtrue;
+}
+
+void QGL_EnableLogging( qboolean enable ) {
+ // bk001205 - fixed for new countdown
+ static qboolean isEnabled = qfalse; // init
+
+ // return if we're already active
+ if ( isEnabled && enable ) {
+ // decrement log counter and stop if it has reached 0
+ ri.Cvar_Set( "r_logFile", va("%d", r_logFile->integer - 1 ) );
+ if ( r_logFile->integer ) {
+ return;
+ }
+ enable = qfalse;
+ }
+
+ // return if we're already disabled
+ if ( !enable && !isEnabled )
+ return;
+
+ isEnabled = enable;
+
+ // bk001205 - old code starts here
+ if ( enable ) {
+ if ( !glw_state.log_fp ) {
+ struct tm *newtime;
+ time_t aclock;
+ char buffer[1024];
+ cvar_t *basedir;
+
+ time( &aclock );
+ newtime = localtime( &aclock );
+
+ asctime( newtime );
+
+ basedir = ri.Cvar_Get( "fs_basepath", "", 0 ); // FIXME: userdir?
+ assert(basedir);
+ Com_sprintf( buffer, sizeof(buffer), "%s/gl.log", basedir->string );
+ glw_state.log_fp = fopen( buffer, "wt" );
+ assert(glw_state.log_fp);
+ ri.Printf(PRINT_ALL, "QGL_EnableLogging(%d): writing %s\n", r_logFile->integer, buffer );
+
+ fprintf( glw_state.log_fp, "%s\n", asctime( newtime ) );
+ }
+
+ qglAccum = logAccum;
+ qglAlphaFunc = logAlphaFunc;
+ qglAreTexturesResident = logAreTexturesResident;
+ qglArrayElement = logArrayElement;
+ qglBegin = logBegin;
+ qglBindTexture = logBindTexture;
+ qglBitmap = logBitmap;
+ qglBlendFunc = logBlendFunc;
+ qglCallList = logCallList;
+ qglCallLists = logCallLists;
+ qglClear = logClear;
+ qglClearAccum = logClearAccum;
+ qglClearColor = logClearColor;
+ qglClearDepth = logClearDepth;
+ qglClearIndex = logClearIndex;
+ qglClearStencil = logClearStencil;
+ qglClipPlane = logClipPlane;
+ qglColor3b = logColor3b;
+ qglColor3bv = logColor3bv;
+ qglColor3d = logColor3d;
+ qglColor3dv = logColor3dv;
+ qglColor3f = logColor3f;
+ qglColor3fv = logColor3fv;
+ qglColor3i = logColor3i;
+ qglColor3iv = logColor3iv;
+ qglColor3s = logColor3s;
+ qglColor3sv = logColor3sv;
+ qglColor3ub = logColor3ub;
+ qglColor3ubv = logColor3ubv;
+ qglColor3ui = logColor3ui;
+ qglColor3uiv = logColor3uiv;
+ qglColor3us = logColor3us;
+ qglColor3usv = logColor3usv;
+ qglColor4b = logColor4b;
+ qglColor4bv = logColor4bv;
+ qglColor4d = logColor4d;
+ qglColor4dv = logColor4dv;
+ qglColor4f = logColor4f;
+ qglColor4fv = logColor4fv;
+ qglColor4i = logColor4i;
+ qglColor4iv = logColor4iv;
+ qglColor4s = logColor4s;
+ qglColor4sv = logColor4sv;
+ qglColor4ub = logColor4ub;
+ qglColor4ubv = logColor4ubv;
+ qglColor4ui = logColor4ui;
+ qglColor4uiv = logColor4uiv;
+ qglColor4us = logColor4us;
+ qglColor4usv = logColor4usv;
+ qglColorMask = logColorMask;
+ qglColorMaterial = logColorMaterial;
+ qglColorPointer = logColorPointer;
+ qglCopyPixels = logCopyPixels;
+ qglCopyTexImage1D = logCopyTexImage1D;
+ qglCopyTexImage2D = logCopyTexImage2D;
+ qglCopyTexSubImage1D = logCopyTexSubImage1D;
+ qglCopyTexSubImage2D = logCopyTexSubImage2D;
+ qglCullFace = logCullFace;
+ qglDeleteLists = logDeleteLists ;
+ qglDeleteTextures = logDeleteTextures ;
+ qglDepthFunc = logDepthFunc ;
+ qglDepthMask = logDepthMask ;
+ qglDepthRange = logDepthRange ;
+ qglDisable = logDisable ;
+ qglDisableClientState = logDisableClientState ;
+ qglDrawArrays = logDrawArrays ;
+ qglDrawBuffer = logDrawBuffer ;
+ qglDrawElements = logDrawElements ;
+ qglDrawPixels = logDrawPixels ;
+ qglEdgeFlag = logEdgeFlag ;
+ qglEdgeFlagPointer = logEdgeFlagPointer ;
+ qglEdgeFlagv = logEdgeFlagv ;
+ qglEnable = logEnable ;
+ qglEnableClientState = logEnableClientState ;
+ qglEnd = logEnd ;
+ qglEndList = logEndList ;
+ qglEvalCoord1d = logEvalCoord1d ;
+ qglEvalCoord1dv = logEvalCoord1dv ;
+ qglEvalCoord1f = logEvalCoord1f ;
+ qglEvalCoord1fv = logEvalCoord1fv ;
+ qglEvalCoord2d = logEvalCoord2d ;
+ qglEvalCoord2dv = logEvalCoord2dv ;
+ qglEvalCoord2f = logEvalCoord2f ;
+ qglEvalCoord2fv = logEvalCoord2fv ;
+ qglEvalMesh1 = logEvalMesh1 ;
+ qglEvalMesh2 = logEvalMesh2 ;
+ qglEvalPoint1 = logEvalPoint1 ;
+ qglEvalPoint2 = logEvalPoint2 ;
+ qglFeedbackBuffer = logFeedbackBuffer ;
+ qglFinish = logFinish ;
+ qglFlush = logFlush ;
+ qglFogf = logFogf ;
+ qglFogfv = logFogfv ;
+ qglFogi = logFogi ;
+ qglFogiv = logFogiv ;
+ qglFrontFace = logFrontFace ;
+ qglFrustum = logFrustum ;
+ qglGenLists = logGenLists ;
+ qglGenTextures = logGenTextures ;
+ qglGetBooleanv = logGetBooleanv ;
+ qglGetClipPlane = logGetClipPlane ;
+ qglGetDoublev = logGetDoublev ;
+ qglGetError = logGetError ;
+ qglGetFloatv = logGetFloatv ;
+ qglGetIntegerv = logGetIntegerv ;
+ qglGetLightfv = logGetLightfv ;
+ qglGetLightiv = logGetLightiv ;
+ qglGetMapdv = logGetMapdv ;
+ qglGetMapfv = logGetMapfv ;
+ qglGetMapiv = logGetMapiv ;
+ qglGetMaterialfv = logGetMaterialfv ;
+ qglGetMaterialiv = logGetMaterialiv ;
+ qglGetPixelMapfv = logGetPixelMapfv ;
+ qglGetPixelMapuiv = logGetPixelMapuiv ;
+ qglGetPixelMapusv = logGetPixelMapusv ;
+ qglGetPointerv = logGetPointerv ;
+ qglGetPolygonStipple = logGetPolygonStipple ;
+ qglGetString = logGetString ;
+ qglGetTexEnvfv = logGetTexEnvfv ;
+ qglGetTexEnviv = logGetTexEnviv ;
+ qglGetTexGendv = logGetTexGendv ;
+ qglGetTexGenfv = logGetTexGenfv ;
+ qglGetTexGeniv = logGetTexGeniv ;
+ qglGetTexImage = logGetTexImage ;
+ qglGetTexLevelParameterfv = logGetTexLevelParameterfv ;
+ qglGetTexLevelParameteriv = logGetTexLevelParameteriv ;
+ qglGetTexParameterfv = logGetTexParameterfv ;
+ qglGetTexParameteriv = logGetTexParameteriv ;
+ qglHint = logHint ;
+ qglIndexMask = logIndexMask ;
+ qglIndexPointer = logIndexPointer ;
+ qglIndexd = logIndexd ;
+ qglIndexdv = logIndexdv ;
+ qglIndexf = logIndexf ;
+ qglIndexfv = logIndexfv ;
+ qglIndexi = logIndexi ;
+ qglIndexiv = logIndexiv ;
+ qglIndexs = logIndexs ;
+ qglIndexsv = logIndexsv ;
+ qglIndexub = logIndexub ;
+ qglIndexubv = logIndexubv ;
+ qglInitNames = logInitNames ;
+ qglInterleavedArrays = logInterleavedArrays ;
+ qglIsEnabled = logIsEnabled ;
+ qglIsList = logIsList ;
+ qglIsTexture = logIsTexture ;
+ qglLightModelf = logLightModelf ;
+ qglLightModelfv = logLightModelfv ;
+ qglLightModeli = logLightModeli ;
+ qglLightModeliv = logLightModeliv ;
+ qglLightf = logLightf ;
+ qglLightfv = logLightfv ;
+ qglLighti = logLighti ;
+ qglLightiv = logLightiv ;
+ qglLineStipple = logLineStipple ;
+ qglLineWidth = logLineWidth ;
+ qglListBase = logListBase ;
+ qglLoadIdentity = logLoadIdentity ;
+ qglLoadMatrixd = logLoadMatrixd ;
+ qglLoadMatrixf = logLoadMatrixf ;
+ qglLoadName = logLoadName ;
+ qglLogicOp = logLogicOp ;
+ qglMap1d = logMap1d ;
+ qglMap1f = logMap1f ;
+ qglMap2d = logMap2d ;
+ qglMap2f = logMap2f ;
+ qglMapGrid1d = logMapGrid1d ;
+ qglMapGrid1f = logMapGrid1f ;
+ qglMapGrid2d = logMapGrid2d ;
+ qglMapGrid2f = logMapGrid2f ;
+ qglMaterialf = logMaterialf ;
+ qglMaterialfv = logMaterialfv ;
+ qglMateriali = logMateriali ;
+ qglMaterialiv = logMaterialiv ;
+ qglMatrixMode = logMatrixMode ;
+ qglMultMatrixd = logMultMatrixd ;
+ qglMultMatrixf = logMultMatrixf ;
+ qglNewList = logNewList ;
+ qglNormal3b = logNormal3b ;
+ qglNormal3bv = logNormal3bv ;
+ qglNormal3d = logNormal3d ;
+ qglNormal3dv = logNormal3dv ;
+ qglNormal3f = logNormal3f ;
+ qglNormal3fv = logNormal3fv ;
+ qglNormal3i = logNormal3i ;
+ qglNormal3iv = logNormal3iv ;
+ qglNormal3s = logNormal3s ;
+ qglNormal3sv = logNormal3sv ;
+ qglNormalPointer = logNormalPointer ;
+ qglOrtho = logOrtho ;
+ qglPassThrough = logPassThrough ;
+ qglPixelMapfv = logPixelMapfv ;
+ qglPixelMapuiv = logPixelMapuiv ;
+ qglPixelMapusv = logPixelMapusv ;
+ qglPixelStoref = logPixelStoref ;
+ qglPixelStorei = logPixelStorei ;
+ qglPixelTransferf = logPixelTransferf ;
+ qglPixelTransferi = logPixelTransferi ;
+ qglPixelZoom = logPixelZoom ;
+ qglPointSize = logPointSize ;
+ qglPolygonMode = logPolygonMode ;
+ qglPolygonOffset = logPolygonOffset ;
+ qglPolygonStipple = logPolygonStipple ;
+ qglPopAttrib = logPopAttrib ;
+ qglPopClientAttrib = logPopClientAttrib ;
+ qglPopMatrix = logPopMatrix ;
+ qglPopName = logPopName ;
+ qglPrioritizeTextures = logPrioritizeTextures ;
+ qglPushAttrib = logPushAttrib ;
+ qglPushClientAttrib = logPushClientAttrib ;
+ qglPushMatrix = logPushMatrix ;
+ qglPushName = logPushName ;
+ qglRasterPos2d = logRasterPos2d ;
+ qglRasterPos2dv = logRasterPos2dv ;
+ qglRasterPos2f = logRasterPos2f ;
+ qglRasterPos2fv = logRasterPos2fv ;
+ qglRasterPos2i = logRasterPos2i ;
+ qglRasterPos2iv = logRasterPos2iv ;
+ qglRasterPos2s = logRasterPos2s ;
+ qglRasterPos2sv = logRasterPos2sv ;
+ qglRasterPos3d = logRasterPos3d ;
+ qglRasterPos3dv = logRasterPos3dv ;
+ qglRasterPos3f = logRasterPos3f ;
+ qglRasterPos3fv = logRasterPos3fv ;
+ qglRasterPos3i = logRasterPos3i ;
+ qglRasterPos3iv = logRasterPos3iv ;
+ qglRasterPos3s = logRasterPos3s ;
+ qglRasterPos3sv = logRasterPos3sv ;
+ qglRasterPos4d = logRasterPos4d ;
+ qglRasterPos4dv = logRasterPos4dv ;
+ qglRasterPos4f = logRasterPos4f ;
+ qglRasterPos4fv = logRasterPos4fv ;
+ qglRasterPos4i = logRasterPos4i ;
+ qglRasterPos4iv = logRasterPos4iv ;
+ qglRasterPos4s = logRasterPos4s ;
+ qglRasterPos4sv = logRasterPos4sv ;
+ qglReadBuffer = logReadBuffer ;
+ qglReadPixels = logReadPixels ;
+ qglRectd = logRectd ;
+ qglRectdv = logRectdv ;
+ qglRectf = logRectf ;
+ qglRectfv = logRectfv ;
+ qglRecti = logRecti ;
+ qglRectiv = logRectiv ;
+ qglRects = logRects ;
+ qglRectsv = logRectsv ;
+ qglRenderMode = logRenderMode ;
+ qglRotated = logRotated ;
+ qglRotatef = logRotatef ;
+ qglScaled = logScaled ;
+ qglScalef = logScalef ;
+ qglScissor = logScissor ;
+ qglSelectBuffer = logSelectBuffer ;
+ qglShadeModel = logShadeModel ;
+ qglStencilFunc = logStencilFunc ;
+ qglStencilMask = logStencilMask ;
+ qglStencilOp = logStencilOp ;
+ qglTexCoord1d = logTexCoord1d ;
+ qglTexCoord1dv = logTexCoord1dv ;
+ qglTexCoord1f = logTexCoord1f ;
+ qglTexCoord1fv = logTexCoord1fv ;
+ qglTexCoord1i = logTexCoord1i ;
+ qglTexCoord1iv = logTexCoord1iv ;
+ qglTexCoord1s = logTexCoord1s ;
+ qglTexCoord1sv = logTexCoord1sv ;
+ qglTexCoord2d = logTexCoord2d ;
+ qglTexCoord2dv = logTexCoord2dv ;
+ qglTexCoord2f = logTexCoord2f ;
+ qglTexCoord2fv = logTexCoord2fv ;
+ qglTexCoord2i = logTexCoord2i ;
+ qglTexCoord2iv = logTexCoord2iv ;
+ qglTexCoord2s = logTexCoord2s ;
+ qglTexCoord2sv = logTexCoord2sv ;
+ qglTexCoord3d = logTexCoord3d ;
+ qglTexCoord3dv = logTexCoord3dv ;
+ qglTexCoord3f = logTexCoord3f ;
+ qglTexCoord3fv = logTexCoord3fv ;
+ qglTexCoord3i = logTexCoord3i ;
+ qglTexCoord3iv = logTexCoord3iv ;
+ qglTexCoord3s = logTexCoord3s ;
+ qglTexCoord3sv = logTexCoord3sv ;
+ qglTexCoord4d = logTexCoord4d ;
+ qglTexCoord4dv = logTexCoord4dv ;
+ qglTexCoord4f = logTexCoord4f ;
+ qglTexCoord4fv = logTexCoord4fv ;
+ qglTexCoord4i = logTexCoord4i ;
+ qglTexCoord4iv = logTexCoord4iv ;
+ qglTexCoord4s = logTexCoord4s ;
+ qglTexCoord4sv = logTexCoord4sv ;
+ qglTexCoordPointer = logTexCoordPointer ;
+ qglTexEnvf = logTexEnvf ;
+ qglTexEnvfv = logTexEnvfv ;
+ qglTexEnvi = logTexEnvi ;
+ qglTexEnviv = logTexEnviv ;
+ qglTexGend = logTexGend ;
+ qglTexGendv = logTexGendv ;
+ qglTexGenf = logTexGenf ;
+ qglTexGenfv = logTexGenfv ;
+ qglTexGeni = logTexGeni ;
+ qglTexGeniv = logTexGeniv ;
+ qglTexImage1D = logTexImage1D ;
+ qglTexImage2D = logTexImage2D ;
+ qglTexParameterf = logTexParameterf ;
+ qglTexParameterfv = logTexParameterfv ;
+ qglTexParameteri = logTexParameteri ;
+ qglTexParameteriv = logTexParameteriv ;
+ qglTexSubImage1D = logTexSubImage1D ;
+ qglTexSubImage2D = logTexSubImage2D ;
+ qglTranslated = logTranslated ;
+ qglTranslatef = logTranslatef ;
+ qglVertex2d = logVertex2d ;
+ qglVertex2dv = logVertex2dv ;
+ qglVertex2f = logVertex2f ;
+ qglVertex2fv = logVertex2fv ;
+ qglVertex2i = logVertex2i ;
+ qglVertex2iv = logVertex2iv ;
+ qglVertex2s = logVertex2s ;
+ qglVertex2sv = logVertex2sv ;
+ qglVertex3d = logVertex3d ;
+ qglVertex3dv = logVertex3dv ;
+ qglVertex3f = logVertex3f ;
+ qglVertex3fv = logVertex3fv ;
+ qglVertex3i = logVertex3i ;
+ qglVertex3iv = logVertex3iv ;
+ qglVertex3s = logVertex3s ;
+ qglVertex3sv = logVertex3sv ;
+ qglVertex4d = logVertex4d ;
+ qglVertex4dv = logVertex4dv ;
+ qglVertex4f = logVertex4f ;
+ qglVertex4fv = logVertex4fv ;
+ qglVertex4i = logVertex4i ;
+ qglVertex4iv = logVertex4iv ;
+ qglVertex4s = logVertex4s ;
+ qglVertex4sv = logVertex4sv ;
+ qglVertexPointer = logVertexPointer ;
+ qglViewport = logViewport ;
+ }
+ else
+ {
+ qglAccum = dllAccum;
+ qglAlphaFunc = dllAlphaFunc;
+ qglAreTexturesResident = dllAreTexturesResident;
+ qglArrayElement = dllArrayElement;
+ qglBegin = dllBegin;
+ qglBindTexture = dllBindTexture;
+ qglBitmap = dllBitmap;
+ qglBlendFunc = dllBlendFunc;
+ qglCallList = dllCallList;
+ qglCallLists = dllCallLists;
+ qglClear = dllClear;
+ qglClearAccum = dllClearAccum;
+ qglClearColor = dllClearColor;
+ qglClearDepth = dllClearDepth;
+ qglClearIndex = dllClearIndex;
+ qglClearStencil = dllClearStencil;
+ qglClipPlane = dllClipPlane;
+ qglColor3b = dllColor3b;
+ qglColor3bv = dllColor3bv;
+ qglColor3d = dllColor3d;
+ qglColor3dv = dllColor3dv;
+ qglColor3f = dllColor3f;
+ qglColor3fv = dllColor3fv;
+ qglColor3i = dllColor3i;
+ qglColor3iv = dllColor3iv;
+ qglColor3s = dllColor3s;
+ qglColor3sv = dllColor3sv;
+ qglColor3ub = dllColor3ub;
+ qglColor3ubv = dllColor3ubv;
+ qglColor3ui = dllColor3ui;
+ qglColor3uiv = dllColor3uiv;
+ qglColor3us = dllColor3us;
+ qglColor3usv = dllColor3usv;
+ qglColor4b = dllColor4b;
+ qglColor4bv = dllColor4bv;
+ qglColor4d = dllColor4d;
+ qglColor4dv = dllColor4dv;
+ qglColor4f = dllColor4f;
+ qglColor4fv = dllColor4fv;
+ qglColor4i = dllColor4i;
+ qglColor4iv = dllColor4iv;
+ qglColor4s = dllColor4s;
+ qglColor4sv = dllColor4sv;
+ qglColor4ub = dllColor4ub;
+ qglColor4ubv = dllColor4ubv;
+ qglColor4ui = dllColor4ui;
+ qglColor4uiv = dllColor4uiv;
+ qglColor4us = dllColor4us;
+ qglColor4usv = dllColor4usv;
+ qglColorMask = dllColorMask;
+ qglColorMaterial = dllColorMaterial;
+ qglColorPointer = dllColorPointer;
+ qglCopyPixels = dllCopyPixels;
+ qglCopyTexImage1D = dllCopyTexImage1D;
+ qglCopyTexImage2D = dllCopyTexImage2D;
+ qglCopyTexSubImage1D = dllCopyTexSubImage1D;
+ qglCopyTexSubImage2D = dllCopyTexSubImage2D;
+ qglCullFace = dllCullFace;
+ qglDeleteLists = dllDeleteLists ;
+ qglDeleteTextures = dllDeleteTextures ;
+ qglDepthFunc = dllDepthFunc ;
+ qglDepthMask = dllDepthMask ;
+ qglDepthRange = dllDepthRange ;
+ qglDisable = dllDisable ;
+ qglDisableClientState = dllDisableClientState ;
+ qglDrawArrays = dllDrawArrays ;
+ qglDrawBuffer = dllDrawBuffer ;
+ qglDrawElements = dllDrawElements ;
+ qglDrawPixels = dllDrawPixels ;
+ qglEdgeFlag = dllEdgeFlag ;
+ qglEdgeFlagPointer = dllEdgeFlagPointer ;
+ qglEdgeFlagv = dllEdgeFlagv ;
+ qglEnable = dllEnable ;
+ qglEnableClientState = dllEnableClientState ;
+ qglEnd = dllEnd ;
+ qglEndList = dllEndList ;
+ qglEvalCoord1d = dllEvalCoord1d ;
+ qglEvalCoord1dv = dllEvalCoord1dv ;
+ qglEvalCoord1f = dllEvalCoord1f ;
+ qglEvalCoord1fv = dllEvalCoord1fv ;
+ qglEvalCoord2d = dllEvalCoord2d ;
+ qglEvalCoord2dv = dllEvalCoord2dv ;
+ qglEvalCoord2f = dllEvalCoord2f ;
+ qglEvalCoord2fv = dllEvalCoord2fv ;
+ qglEvalMesh1 = dllEvalMesh1 ;
+ qglEvalMesh2 = dllEvalMesh2 ;
+ qglEvalPoint1 = dllEvalPoint1 ;
+ qglEvalPoint2 = dllEvalPoint2 ;
+ qglFeedbackBuffer = dllFeedbackBuffer ;
+ qglFinish = dllFinish ;
+ qglFlush = dllFlush ;
+ qglFogf = dllFogf ;
+ qglFogfv = dllFogfv ;
+ qglFogi = dllFogi ;
+ qglFogiv = dllFogiv ;
+ qglFrontFace = dllFrontFace ;
+ qglFrustum = dllFrustum ;
+ qglGenLists = dllGenLists ;
+ qglGenTextures = dllGenTextures ;
+ qglGetBooleanv = dllGetBooleanv ;
+ qglGetClipPlane = dllGetClipPlane ;
+ qglGetDoublev = dllGetDoublev ;
+ qglGetError = dllGetError ;
+ qglGetFloatv = dllGetFloatv ;
+ qglGetIntegerv = dllGetIntegerv ;
+ qglGetLightfv = dllGetLightfv ;
+ qglGetLightiv = dllGetLightiv ;
+ qglGetMapdv = dllGetMapdv ;
+ qglGetMapfv = dllGetMapfv ;
+ qglGetMapiv = dllGetMapiv ;
+ qglGetMaterialfv = dllGetMaterialfv ;
+ qglGetMaterialiv = dllGetMaterialiv ;
+ qglGetPixelMapfv = dllGetPixelMapfv ;
+ qglGetPixelMapuiv = dllGetPixelMapuiv ;
+ qglGetPixelMapusv = dllGetPixelMapusv ;
+ qglGetPointerv = dllGetPointerv ;
+ qglGetPolygonStipple = dllGetPolygonStipple ;
+ qglGetString = dllGetString ;
+ qglGetTexEnvfv = dllGetTexEnvfv ;
+ qglGetTexEnviv = dllGetTexEnviv ;
+ qglGetTexGendv = dllGetTexGendv ;
+ qglGetTexGenfv = dllGetTexGenfv ;
+ qglGetTexGeniv = dllGetTexGeniv ;
+ qglGetTexImage = dllGetTexImage ;
+ qglGetTexLevelParameterfv = dllGetTexLevelParameterfv ;
+ qglGetTexLevelParameteriv = dllGetTexLevelParameteriv ;
+ qglGetTexParameterfv = dllGetTexParameterfv ;
+ qglGetTexParameteriv = dllGetTexParameteriv ;
+ qglHint = dllHint ;
+ qglIndexMask = dllIndexMask ;
+ qglIndexPointer = dllIndexPointer ;
+ qglIndexd = dllIndexd ;
+ qglIndexdv = dllIndexdv ;
+ qglIndexf = dllIndexf ;
+ qglIndexfv = dllIndexfv ;
+ qglIndexi = dllIndexi ;
+ qglIndexiv = dllIndexiv ;
+ qglIndexs = dllIndexs ;
+ qglIndexsv = dllIndexsv ;
+ qglIndexub = dllIndexub ;
+ qglIndexubv = dllIndexubv ;
+ qglInitNames = dllInitNames ;
+ qglInterleavedArrays = dllInterleavedArrays ;
+ qglIsEnabled = dllIsEnabled ;
+ qglIsList = dllIsList ;
+ qglIsTexture = dllIsTexture ;
+ qglLightModelf = dllLightModelf ;
+ qglLightModelfv = dllLightModelfv ;
+ qglLightModeli = dllLightModeli ;
+ qglLightModeliv = dllLightModeliv ;
+ qglLightf = dllLightf ;
+ qglLightfv = dllLightfv ;
+ qglLighti = dllLighti ;
+ qglLightiv = dllLightiv ;
+ qglLineStipple = dllLineStipple ;
+ qglLineWidth = dllLineWidth ;
+ qglListBase = dllListBase ;
+ qglLoadIdentity = dllLoadIdentity ;
+ qglLoadMatrixd = dllLoadMatrixd ;
+ qglLoadMatrixf = dllLoadMatrixf ;
+ qglLoadName = dllLoadName ;
+ qglLogicOp = dllLogicOp ;
+ qglMap1d = dllMap1d ;
+ qglMap1f = dllMap1f ;
+ qglMap2d = dllMap2d ;
+ qglMap2f = dllMap2f ;
+ qglMapGrid1d = dllMapGrid1d ;
+ qglMapGrid1f = dllMapGrid1f ;
+ qglMapGrid2d = dllMapGrid2d ;
+ qglMapGrid2f = dllMapGrid2f ;
+ qglMaterialf = dllMaterialf ;
+ qglMaterialfv = dllMaterialfv ;
+ qglMateriali = dllMateriali ;
+ qglMaterialiv = dllMaterialiv ;
+ qglMatrixMode = dllMatrixMode ;
+ qglMultMatrixd = dllMultMatrixd ;
+ qglMultMatrixf = dllMultMatrixf ;
+ qglNewList = dllNewList ;
+ qglNormal3b = dllNormal3b ;
+ qglNormal3bv = dllNormal3bv ;
+ qglNormal3d = dllNormal3d ;
+ qglNormal3dv = dllNormal3dv ;
+ qglNormal3f = dllNormal3f ;
+ qglNormal3fv = dllNormal3fv ;
+ qglNormal3i = dllNormal3i ;
+ qglNormal3iv = dllNormal3iv ;
+ qglNormal3s = dllNormal3s ;
+ qglNormal3sv = dllNormal3sv ;
+ qglNormalPointer = dllNormalPointer ;
+ qglOrtho = dllOrtho ;
+ qglPassThrough = dllPassThrough ;
+ qglPixelMapfv = dllPixelMapfv ;
+ qglPixelMapuiv = dllPixelMapuiv ;
+ qglPixelMapusv = dllPixelMapusv ;
+ qglPixelStoref = dllPixelStoref ;
+ qglPixelStorei = dllPixelStorei ;
+ qglPixelTransferf = dllPixelTransferf ;
+ qglPixelTransferi = dllPixelTransferi ;
+ qglPixelZoom = dllPixelZoom ;
+ qglPointSize = dllPointSize ;
+ qglPolygonMode = dllPolygonMode ;
+ qglPolygonOffset = dllPolygonOffset ;
+ qglPolygonStipple = dllPolygonStipple ;
+ qglPopAttrib = dllPopAttrib ;
+ qglPopClientAttrib = dllPopClientAttrib ;
+ qglPopMatrix = dllPopMatrix ;
+ qglPopName = dllPopName ;
+ qglPrioritizeTextures = dllPrioritizeTextures ;
+ qglPushAttrib = dllPushAttrib ;
+ qglPushClientAttrib = dllPushClientAttrib ;
+ qglPushMatrix = dllPushMatrix ;
+ qglPushName = dllPushName ;
+ qglRasterPos2d = dllRasterPos2d ;
+ qglRasterPos2dv = dllRasterPos2dv ;
+ qglRasterPos2f = dllRasterPos2f ;
+ qglRasterPos2fv = dllRasterPos2fv ;
+ qglRasterPos2i = dllRasterPos2i ;
+ qglRasterPos2iv = dllRasterPos2iv ;
+ qglRasterPos2s = dllRasterPos2s ;
+ qglRasterPos2sv = dllRasterPos2sv ;
+ qglRasterPos3d = dllRasterPos3d ;
+ qglRasterPos3dv = dllRasterPos3dv ;
+ qglRasterPos3f = dllRasterPos3f ;
+ qglRasterPos3fv = dllRasterPos3fv ;
+ qglRasterPos3i = dllRasterPos3i ;
+ qglRasterPos3iv = dllRasterPos3iv ;
+ qglRasterPos3s = dllRasterPos3s ;
+ qglRasterPos3sv = dllRasterPos3sv ;
+ qglRasterPos4d = dllRasterPos4d ;
+ qglRasterPos4dv = dllRasterPos4dv ;
+ qglRasterPos4f = dllRasterPos4f ;
+ qglRasterPos4fv = dllRasterPos4fv ;
+ qglRasterPos4i = dllRasterPos4i ;
+ qglRasterPos4iv = dllRasterPos4iv ;
+ qglRasterPos4s = dllRasterPos4s ;
+ qglRasterPos4sv = dllRasterPos4sv ;
+ qglReadBuffer = dllReadBuffer ;
+ qglReadPixels = dllReadPixels ;
+ qglRectd = dllRectd ;
+ qglRectdv = dllRectdv ;
+ qglRectf = dllRectf ;
+ qglRectfv = dllRectfv ;
+ qglRecti = dllRecti ;
+ qglRectiv = dllRectiv ;
+ qglRects = dllRects ;
+ qglRectsv = dllRectsv ;
+ qglRenderMode = dllRenderMode ;
+ qglRotated = dllRotated ;
+ qglRotatef = dllRotatef ;
+ qglScaled = dllScaled ;
+ qglScalef = dllScalef ;
+ qglScissor = dllScissor ;
+ qglSelectBuffer = dllSelectBuffer ;
+ qglShadeModel = dllShadeModel ;
+ qglStencilFunc = dllStencilFunc ;
+ qglStencilMask = dllStencilMask ;
+ qglStencilOp = dllStencilOp ;
+ qglTexCoord1d = dllTexCoord1d ;
+ qglTexCoord1dv = dllTexCoord1dv ;
+ qglTexCoord1f = dllTexCoord1f ;
+ qglTexCoord1fv = dllTexCoord1fv ;
+ qglTexCoord1i = dllTexCoord1i ;
+ qglTexCoord1iv = dllTexCoord1iv ;
+ qglTexCoord1s = dllTexCoord1s ;
+ qglTexCoord1sv = dllTexCoord1sv ;
+ qglTexCoord2d = dllTexCoord2d ;
+ qglTexCoord2dv = dllTexCoord2dv ;
+ qglTexCoord2f = dllTexCoord2f ;
+ qglTexCoord2fv = dllTexCoord2fv ;
+ qglTexCoord2i = dllTexCoord2i ;
+ qglTexCoord2iv = dllTexCoord2iv ;
+ qglTexCoord2s = dllTexCoord2s ;
+ qglTexCoord2sv = dllTexCoord2sv ;
+ qglTexCoord3d = dllTexCoord3d ;
+ qglTexCoord3dv = dllTexCoord3dv ;
+ qglTexCoord3f = dllTexCoord3f ;
+ qglTexCoord3fv = dllTexCoord3fv ;
+ qglTexCoord3i = dllTexCoord3i ;
+ qglTexCoord3iv = dllTexCoord3iv ;
+ qglTexCoord3s = dllTexCoord3s ;
+ qglTexCoord3sv = dllTexCoord3sv ;
+ qglTexCoord4d = dllTexCoord4d ;
+ qglTexCoord4dv = dllTexCoord4dv ;
+ qglTexCoord4f = dllTexCoord4f ;
+ qglTexCoord4fv = dllTexCoord4fv ;
+ qglTexCoord4i = dllTexCoord4i ;
+ qglTexCoord4iv = dllTexCoord4iv ;
+ qglTexCoord4s = dllTexCoord4s ;
+ qglTexCoord4sv = dllTexCoord4sv ;
+ qglTexCoordPointer = dllTexCoordPointer ;
+ qglTexEnvf = dllTexEnvf ;
+ qglTexEnvfv = dllTexEnvfv ;
+ qglTexEnvi = dllTexEnvi ;
+ qglTexEnviv = dllTexEnviv ;
+ qglTexGend = dllTexGend ;
+ qglTexGendv = dllTexGendv ;
+ qglTexGenf = dllTexGenf ;
+ qglTexGenfv = dllTexGenfv ;
+ qglTexGeni = dllTexGeni ;
+ qglTexGeniv = dllTexGeniv ;
+ qglTexImage1D = dllTexImage1D ;
+ qglTexImage2D = dllTexImage2D ;
+ qglTexParameterf = dllTexParameterf ;
+ qglTexParameterfv = dllTexParameterfv ;
+ qglTexParameteri = dllTexParameteri ;
+ qglTexParameteriv = dllTexParameteriv ;
+ qglTexSubImage1D = dllTexSubImage1D ;
+ qglTexSubImage2D = dllTexSubImage2D ;
+ qglTranslated = dllTranslated ;
+ qglTranslatef = dllTranslatef ;
+ qglVertex2d = dllVertex2d ;
+ qglVertex2dv = dllVertex2dv ;
+ qglVertex2f = dllVertex2f ;
+ qglVertex2fv = dllVertex2fv ;
+ qglVertex2i = dllVertex2i ;
+ qglVertex2iv = dllVertex2iv ;
+ qglVertex2s = dllVertex2s ;
+ qglVertex2sv = dllVertex2sv ;
+ qglVertex3d = dllVertex3d ;
+ qglVertex3dv = dllVertex3dv ;
+ qglVertex3f = dllVertex3f ;
+ qglVertex3fv = dllVertex3fv ;
+ qglVertex3i = dllVertex3i ;
+ qglVertex3iv = dllVertex3iv ;
+ qglVertex3s = dllVertex3s ;
+ qglVertex3sv = dllVertex3sv ;
+ qglVertex4d = dllVertex4d ;
+ qglVertex4dv = dllVertex4dv ;
+ qglVertex4f = dllVertex4f ;
+ qglVertex4fv = dllVertex4fv ;
+ qglVertex4i = dllVertex4i ;
+ qglVertex4iv = dllVertex4iv ;
+ qglVertex4s = dllVertex4s ;
+ qglVertex4sv = dllVertex4sv ;
+ qglVertexPointer = dllVertexPointer ;
+ qglViewport = dllViewport ;
+ }
+}
+
+
+void GLimp_LogNewFrame( void )
+{
+ fprintf( glw_state.log_fp, "*** R_BeginFrame ***\n" );
+}
+
+
diff --git a/code/unix/linux_signals.c b/code/unix/linux_signals.c
new file mode 100755
index 0000000..279fbad
--- /dev/null
+++ b/code/unix/linux_signals.c
@@ -0,0 +1,61 @@
+/*
+===========================================================================
+Copyright (C) 1999-2005 Id Software, Inc.
+
+This file is part of Quake III Arena source code.
+
+Quake III Arena source code is free software; you can redistribute it
+and/or modify it under the terms of the GNU General Public License as
+published by the Free Software Foundation; either version 2 of the License,
+or (at your option) any later version.
+
+Quake III Arena source code is distributed in the hope that it will be
+useful, but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with Foobar; if not, write to the Free Software
+Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+===========================================================================
+*/
+#include <signal.h>
+
+#include "../game/q_shared.h"
+#include "../qcommon/qcommon.h"
+#ifndef DEDICATED
+#include "../renderer/tr_local.h"
+#endif
+
+static qboolean signalcaught = qfalse;;
+
+void Sys_Exit(int); // bk010104 - abstraction
+
+static void signal_handler(int sig) // bk010104 - replace this... (NOTE TTimo huh?)
+{
+ if (signalcaught)
+ {
+ printf("DOUBLE SIGNAL FAULT: Received signal %d, exiting...\n", sig);
+ Sys_Exit(1); // bk010104 - abstraction
+ }
+
+ signalcaught = qtrue;
+ printf("Received signal %d, exiting...\n", sig);
+#ifndef DEDICATED
+ GLimp_Shutdown(); // bk010104 - shouldn't this be CL_Shutdown
+#endif
+ Sys_Exit(0); // bk010104 - abstraction NOTE TTimo send a 0 to avoid DOUBLE SIGNAL FAULT
+}
+
+void InitSig(void)
+{
+ signal(SIGHUP, signal_handler);
+ signal(SIGQUIT, signal_handler);
+ signal(SIGILL, signal_handler);
+ signal(SIGTRAP, signal_handler);
+ signal(SIGIOT, signal_handler);
+ signal(SIGBUS, signal_handler);
+ signal(SIGFPE, signal_handler);
+ signal(SIGSEGV, signal_handler);
+ signal(SIGTERM, signal_handler);
+}
diff --git a/code/unix/linux_snd.c b/code/unix/linux_snd.c
new file mode 100755
index 0000000..8c3b6b8
--- /dev/null
+++ b/code/unix/linux_snd.c
@@ -0,0 +1,293 @@
+/*
+===========================================================================
+Copyright (C) 1999-2005 Id Software, Inc.
+
+This file is part of Quake III Arena source code.
+
+Quake III Arena source code is free software; you can redistribute it
+and/or modify it under the terms of the GNU General Public License as
+published by the Free Software Foundation; either version 2 of the License,
+or (at your option) any later version.
+
+Quake III Arena source code is distributed in the hope that it will be
+useful, but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with Foobar; if not, write to the Free Software
+Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+===========================================================================
+*/
+#include <unistd.h>
+#include <fcntl.h>
+#include <stdlib.h>
+#include <sys/types.h>
+#include <sys/ioctl.h>
+#include <sys/mman.h>
+#include <sys/shm.h>
+#include <sys/wait.h>
+#ifdef __linux__ // rb0101023 - guard this
+#include <linux/soundcard.h>
+#endif
+#ifdef __FreeBSD__ // rb0101023 - added
+#include <sys/soundcard.h>
+#endif
+#include <stdio.h>
+
+#include "../game/q_shared.h"
+#include "../client/snd_local.h"
+
+int audio_fd;
+int snd_inited=0;
+
+cvar_t *sndbits;
+cvar_t *sndspeed;
+cvar_t *sndchannels;
+
+cvar_t *snddevice;
+
+/* Some devices may work only with 48000 */
+static int tryrates[] = { 22050, 11025, 44100, 48000, 8000 };
+
+static qboolean use_custom_memset = qfalse;
+// https://zerowing.idsoftware.com/bugzilla/show_bug.cgi?id=371
+void Snd_Memset (void* dest, const int val, const size_t count)
+{
+ int *pDest;
+ int i, iterate;
+
+ if (!use_custom_memset)
+ {
+ Com_Memset(dest,val,count);
+ return;
+ }
+ iterate = count / sizeof(int);
+ pDest = (int*)dest;
+ for(i=0; i<iterate; i++)
+ {
+ pDest[i] = val;
+ }
+}
+
+qboolean SNDDMA_Init(void)
+{
+ int rc;
+ int fmt;
+ int tmp;
+ int i;
+ // char *s; // bk001204 - unused
+ struct audio_buf_info info;
+ int caps;
+ extern uid_t saved_euid;
+
+ if (snd_inited)
+ return 1;
+
+ if (!snddevice) {
+ sndbits = Cvar_Get("sndbits", "16", CVAR_ARCHIVE);
+ sndspeed = Cvar_Get("sndspeed", "0", CVAR_ARCHIVE);
+ sndchannels = Cvar_Get("sndchannels", "2", CVAR_ARCHIVE);
+ snddevice = Cvar_Get("snddevice", "/dev/dsp", CVAR_ARCHIVE);
+ }
+
+ // open /dev/dsp, confirm capability to mmap, and get size of dma buffer
+ if (!audio_fd) {
+ seteuid(saved_euid);
+
+ audio_fd = open(snddevice->string, O_RDWR);
+
+ seteuid(getuid());
+
+ if (audio_fd < 0) {
+ perror(snddevice->string);
+ Com_Printf("Could not open %s\n", snddevice->string);
+ return 0;
+ }
+ }
+
+ if (ioctl(audio_fd, SNDCTL_DSP_GETCAPS, &caps) == -1) {
+ perror(snddevice->string);
+ Com_Printf("Sound driver too old\n");
+ close(audio_fd);
+ return 0;
+ }
+
+ if (!(caps & DSP_CAP_TRIGGER) || !(caps & DSP_CAP_MMAP)) {
+ Com_Printf("Sorry but your soundcard can't do this\n");
+ close(audio_fd);
+ return 0;
+ }
+
+
+ /* SNDCTL_DSP_GETOSPACE moved to be called later */
+
+ // set sample bits & speed
+ dma.samplebits = (int)sndbits->value;
+ if (dma.samplebits != 16 && dma.samplebits != 8) {
+ ioctl(audio_fd, SNDCTL_DSP_GETFMTS, &fmt);
+ if (fmt & AFMT_S16_LE)
+ dma.samplebits = 16;
+ else if (fmt & AFMT_U8)
+ dma.samplebits = 8;
+ }
+
+ dma.speed = (int)sndspeed->value;
+ if (!dma.speed) {
+ for (i=0 ; i<sizeof(tryrates)/4 ; i++)
+ if (!ioctl(audio_fd, SNDCTL_DSP_SPEED, &tryrates[i]))
+ break;
+ dma.speed = tryrates[i];
+ }
+
+ dma.channels = (int)sndchannels->value;
+ if (dma.channels < 1 || dma.channels > 2)
+ dma.channels = 2;
+
+/* mmap() call moved forward */
+
+ tmp = 0;
+ if (dma.channels == 2)
+ tmp = 1;
+ rc = ioctl(audio_fd, SNDCTL_DSP_STEREO, &tmp);
+ if (rc < 0) {
+ perror(snddevice->string);
+ Com_Printf("Could not set %s to stereo=%d", snddevice->string, dma.channels);
+ close(audio_fd);
+ return 0;
+ }
+
+ if (tmp)
+ dma.channels = 2;
+ else
+ dma.channels = 1;
+
+ rc = ioctl(audio_fd, SNDCTL_DSP_SPEED, &dma.speed);
+ if (rc < 0) {
+ perror(snddevice->string);
+ Com_Printf("Could not set %s speed to %d", snddevice->string, dma.speed);
+ close(audio_fd);
+ return 0;
+ }
+
+ if (dma.samplebits == 16) {
+ rc = AFMT_S16_LE;
+ rc = ioctl(audio_fd, SNDCTL_DSP_SETFMT, &rc);
+ if (rc < 0) {
+ perror(snddevice->string);
+ Com_Printf("Could not support 16-bit data. Try 8-bit.\n");
+ close(audio_fd);
+ return 0;
+ }
+ } else if (dma.samplebits == 8) {
+ rc = AFMT_U8;
+ rc = ioctl(audio_fd, SNDCTL_DSP_SETFMT, &rc);
+ if (rc < 0) {
+ perror(snddevice->string);
+ Com_Printf("Could not support 8-bit data.\n");
+ close(audio_fd);
+ return 0;
+ }
+ } else {
+ perror(snddevice->string);
+ Com_Printf("%d-bit sound not supported.", dma.samplebits);
+ close(audio_fd);
+ return 0;
+ }
+
+ if (ioctl(audio_fd, SNDCTL_DSP_GETOSPACE, &info)==-1) {
+ perror("GETOSPACE");
+ Com_Printf("Um, can't do GETOSPACE?\n");
+ close(audio_fd);
+ return 0;
+ }
+
+ dma.samples = info.fragstotal * info.fragsize / (dma.samplebits/8);
+ dma.submission_chunk = 1;
+
+ // memory map the dma buffer
+
+ // TTimo 2001/10/08 added PROT_READ to the mmap
+ // https://zerowing.idsoftware.com/bugzilla/show_bug.cgi?id=371
+ // checking Alsa bug, doesn't allow dma alloc with PROT_READ?
+
+ if (!dma.buffer)
+ dma.buffer = (unsigned char *) mmap(NULL, info.fragstotal
+ * info.fragsize, PROT_WRITE|PROT_READ, MAP_FILE|MAP_SHARED, audio_fd, 0);
+
+ if (dma.buffer == MAP_FAILED)
+ {
+ Com_Printf("Could not mmap dma buffer PROT_WRITE|PROT_READ\n");
+ Com_Printf("trying mmap PROT_WRITE (with associated better compatibility / less performance code)\n");
+ dma.buffer = (unsigned char *) mmap(NULL, info.fragstotal
+ * info.fragsize, PROT_WRITE, MAP_FILE|MAP_SHARED, audio_fd, 0);
+ // NOTE TTimo could add a variable to force using regular memset on systems that are known to be safe
+ use_custom_memset = qtrue;
+ }
+
+ if (dma.buffer == MAP_FAILED) {
+ perror(snddevice->string);
+ Com_Printf("Could not mmap %s\n", snddevice->string);
+ close(audio_fd);
+ return 0;
+ }
+
+ // toggle the trigger & start her up
+
+ tmp = 0;
+ rc = ioctl(audio_fd, SNDCTL_DSP_SETTRIGGER, &tmp);
+ if (rc < 0) {
+ perror(snddevice->string);
+ Com_Printf("Could not toggle.\n");
+ close(audio_fd);
+ return 0;
+ }
+
+ tmp = PCM_ENABLE_OUTPUT;
+ rc = ioctl(audio_fd, SNDCTL_DSP_SETTRIGGER, &tmp);
+ if (rc < 0) {
+ perror(snddevice->string);
+ Com_Printf("Could not toggle.\n");
+ close(audio_fd);
+
+ return 0;
+ }
+
+ snd_inited = 1;
+ return 1;
+}
+
+int SNDDMA_GetDMAPos(void)
+{
+ struct count_info count;
+
+ if (!snd_inited) return 0;
+
+ if (ioctl(audio_fd, SNDCTL_DSP_GETOPTR, &count) == -1) {
+ perror(snddevice->string);
+ Com_Printf("Uh, sound dead.\n");
+ close(audio_fd);
+ snd_inited = 0;
+ return 0;
+ }
+ return count.ptr / (dma.samplebits / 8);
+}
+
+void SNDDMA_Shutdown(void)
+{
+}
+
+/*
+==============
+SNDDMA_Submit
+
+Send sound to device if buffer isn't really the dma buffer
+===============
+*/
+void SNDDMA_Submit(void)
+{
+}
+
+void SNDDMA_BeginPainting (void)
+{
+}
diff --git a/code/unix/matha.s b/code/unix/matha.s
new file mode 100755
index 0000000..b193661
--- /dev/null
+++ b/code/unix/matha.s
@@ -0,0 +1,425 @@
+/*
+===========================================================================
+Copyright (C) 1999-2005 Id Software, Inc.
+
+This file is part of Quake III Arena source code.
+
+Quake III Arena source code is free software; you can redistribute it
+and/or modify it under the terms of the GNU General Public License as
+published by the Free Software Foundation; either version 2 of the License,
+or (at your option) any later version.
+
+Quake III Arena source code is distributed in the hope that it will be
+useful, but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with Foobar; if not, write to the Free Software
+Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+===========================================================================
+*/
+//
+// math.s
+// x86 assembly-language math routines.
+
+#define GLQUAKE 1 // don't include unneeded defs
+#include "qasm.h"
+
+
+#if id386
+
+ .data
+
+ .align 4
+Ljmptab: .long Lcase0, Lcase1, Lcase2, Lcase3
+ .long Lcase4, Lcase5, Lcase6, Lcase7
+
+ .text
+
+// TODO: rounding needed?
+// stack parameter offset
+#define val 4
+
+.globl C(Invert24To16)
+C(Invert24To16):
+
+ movl val(%esp),%ecx
+ movl $0x100,%edx // 0x10000000000 as dividend
+ cmpl %edx,%ecx
+ jle LOutOfRange
+
+ subl %eax,%eax
+ divl %ecx
+
+ ret
+
+LOutOfRange:
+ movl $0xFFFFFFFF,%eax
+ ret
+
+#if 0
+
+#define in 4
+#define out 8
+
+ .align 2
+.globl C(TransformVector)
+C(TransformVector):
+ movl in(%esp),%eax
+ movl out(%esp),%edx
+
+ flds (%eax) // in[0]
+ fmuls C(vright) // in[0]*vright[0]
+ flds (%eax) // in[0] | in[0]*vright[0]
+ fmuls C(vup) // in[0]*vup[0] | in[0]*vright[0]
+ flds (%eax) // in[0] | in[0]*vup[0] | in[0]*vright[0]
+ fmuls C(vpn) // in[0]*vpn[0] | in[0]*vup[0] | in[0]*vright[0]
+
+ flds 4(%eax) // in[1] | ...
+ fmuls C(vright)+4 // in[1]*vright[1] | ...
+ flds 4(%eax) // in[1] | in[1]*vright[1] | ...
+ fmuls C(vup)+4 // in[1]*vup[1] | in[1]*vright[1] | ...
+ flds 4(%eax) // in[1] | in[1]*vup[1] | in[1]*vright[1] | ...
+ fmuls C(vpn)+4 // in[1]*vpn[1] | in[1]*vup[1] | in[1]*vright[1] | ...
+ fxch %st(2) // in[1]*vright[1] | in[1]*vup[1] | in[1]*vpn[1] | ...
+
+ faddp %st(0),%st(5) // in[1]*vup[1] | in[1]*vpn[1] | ...
+ faddp %st(0),%st(3) // in[1]*vpn[1] | ...
+ faddp %st(0),%st(1) // vpn_accum | vup_accum | vright_accum
+
+ flds 8(%eax) // in[2] | ...
+ fmuls C(vright)+8 // in[2]*vright[2] | ...
+ flds 8(%eax) // in[2] | in[2]*vright[2] | ...
+ fmuls C(vup)+8 // in[2]*vup[2] | in[2]*vright[2] | ...
+ flds 8(%eax) // in[2] | in[2]*vup[2] | in[2]*vright[2] | ...
+ fmuls C(vpn)+8 // in[2]*vpn[2] | in[2]*vup[2] | in[2]*vright[2] | ...
+ fxch %st(2) // in[2]*vright[2] | in[2]*vup[2] | in[2]*vpn[2] | ...
+
+ faddp %st(0),%st(5) // in[2]*vup[2] | in[2]*vpn[2] | ...
+ faddp %st(0),%st(3) // in[2]*vpn[2] | ...
+ faddp %st(0),%st(1) // vpn_accum | vup_accum | vright_accum
+
+ fstps 8(%edx) // out[2]
+ fstps 4(%edx) // out[1]
+ fstps (%edx) // out[0]
+
+ ret
+
+#endif
+
+#define EMINS 4+4
+#define EMAXS 4+8
+#define P 4+12
+
+ .align 2
+.globl C(BoxOnPlaneSide)
+C(BoxOnPlaneSide):
+ pushl %ebx
+
+ movl P(%esp),%edx
+ movl EMINS(%esp),%ecx
+ xorl %eax,%eax
+ movl EMAXS(%esp),%ebx
+ movb pl_signbits(%edx),%al
+ cmpb $8,%al
+ jge Lerror
+ flds pl_normal(%edx) // p->normal[0]
+ fld %st(0) // p->normal[0] | p->normal[0]
+ // bk000422 - warning: missing prefix `*' in absolute indirect address, maybe misassembled!
+ // bk001129 - fix from Andrew Henderson, was: Ljmptab(,%eax,4)
+ jmp *Ljmptab(,%eax,4)
+
+
+//dist1= p->normal[0]*emaxs[0] + p->normal[1]*emaxs[1] + p->normal[2]*emaxs[2];
+//dist2= p->normal[0]*emins[0] + p->normal[1]*emins[1] + p->normal[2]*emins[2];
+Lcase0:
+ fmuls (%ebx) // p->normal[0]*emaxs[0] | p->normal[0]
+ flds pl_normal+4(%edx) // p->normal[1] | p->normal[0]*emaxs[0] |
+ // p->normal[0]
+ fxch %st(2) // p->normal[0] | p->normal[0]*emaxs[0] |
+ // p->normal[1]
+ fmuls (%ecx) // p->normal[0]*emins[0] |
+ // p->normal[0]*emaxs[0] | p->normal[1]
+ fxch %st(2) // p->normal[1] | p->normal[0]*emaxs[0] |
+ // p->normal[0]*emins[0]
+ fld %st(0) // p->normal[1] | p->normal[1] |
+ // p->normal[0]*emaxs[0] |
+ // p->normal[0]*emins[0]
+ fmuls 4(%ebx) // p->normal[1]*emaxs[1] | p->normal[1] |
+ // p->normal[0]*emaxs[0] |
+ // p->normal[0]*emins[0]
+ flds pl_normal+8(%edx) // p->normal[2] | p->normal[1]*emaxs[1] |
+ // p->normal[1] | p->normal[0]*emaxs[0] |
+ // p->normal[0]*emins[0]
+ fxch %st(2) // p->normal[1] | p->normal[1]*emaxs[1] |
+ // p->normal[2] | p->normal[0]*emaxs[0] |
+ // p->normal[0]*emins[0]
+ fmuls 4(%ecx) // p->normal[1]*emins[1] |
+ // p->normal[1]*emaxs[1] |
+ // p->normal[2] | p->normal[0]*emaxs[0] |
+ // p->normal[0]*emins[0]
+ fxch %st(2) // p->normal[2] | p->normal[1]*emaxs[1] |
+ // p->normal[1]*emins[1] |
+ // p->normal[0]*emaxs[0] |
+ // p->normal[0]*emins[0]
+ fld %st(0) // p->normal[2] | p->normal[2] |
+ // p->normal[1]*emaxs[1] |
+ // p->normal[1]*emins[1] |
+ // p->normal[0]*emaxs[0] |
+ // p->normal[0]*emins[0]
+ fmuls 8(%ebx) // p->normal[2]*emaxs[2] |
+ // p->normal[2] |
+ // p->normal[1]*emaxs[1] |
+ // p->normal[1]*emins[1] |
+ // p->normal[0]*emaxs[0] |
+ // p->normal[0]*emins[0]
+ fxch %st(5) // p->normal[0]*emins[0] |
+ // p->normal[2] |
+ // p->normal[1]*emaxs[1] |
+ // p->normal[1]*emins[1] |
+ // p->normal[0]*emaxs[0] |
+ // p->normal[2]*emaxs[2]
+ faddp %st(0),%st(3) //p->normal[2] |
+ // p->normal[1]*emaxs[1] |
+ // p->normal[1]*emins[1]+p->normal[0]*emins[0]|
+ // p->normal[0]*emaxs[0] |
+ // p->normal[2]*emaxs[2]
+ fmuls 8(%ecx) //p->normal[2]*emins[2] |
+ // p->normal[1]*emaxs[1] |
+ // p->normal[1]*emins[1]+p->normal[0]*emins[0]|
+ // p->normal[0]*emaxs[0] |
+ // p->normal[2]*emaxs[2]
+ fxch %st(1) //p->normal[1]*emaxs[1] |
+ // p->normal[2]*emins[2] |
+ // p->normal[1]*emins[1]+p->normal[0]*emins[0]|
+ // p->normal[0]*emaxs[0] |
+ // p->normal[2]*emaxs[2]
+ faddp %st(0),%st(3) //p->normal[2]*emins[2] |
+ // p->normal[1]*emins[1]+p->normal[0]*emins[0]|
+ // p->normal[0]*emaxs[0]+p->normal[1]*emaxs[1]|
+ // p->normal[2]*emaxs[2]
+ fxch %st(3) //p->normal[2]*emaxs[2] +
+ // p->normal[1]*emins[1]+p->normal[0]*emins[0]|
+ // p->normal[0]*emaxs[0]+p->normal[1]*emaxs[1]|
+ // p->normal[2]*emins[2]
+ faddp %st(0),%st(2) //p->normal[1]*emins[1]+p->normal[0]*emins[0]|
+ // dist1 | p->normal[2]*emins[2]
+
+ jmp LSetSides
+
+//dist1= p->normal[0]*emins[0] + p->normal[1]*emaxs[1] + p->normal[2]*emaxs[2];
+//dist2= p->normal[0]*emaxs[0] + p->normal[1]*emins[1] + p->normal[2]*emins[2];
+Lcase1:
+ fmuls (%ecx) // emins[0]
+ flds pl_normal+4(%edx)
+ fxch %st(2)
+ fmuls (%ebx) // emaxs[0]
+ fxch %st(2)
+ fld %st(0)
+ fmuls 4(%ebx) // emaxs[1]
+ flds pl_normal+8(%edx)
+ fxch %st(2)
+ fmuls 4(%ecx) // emins[1]
+ fxch %st(2)
+ fld %st(0)
+ fmuls 8(%ebx) // emaxs[2]
+ fxch %st(5)
+ faddp %st(0),%st(3)
+ fmuls 8(%ecx) // emins[2]
+ fxch %st(1)
+ faddp %st(0),%st(3)
+ fxch %st(3)
+ faddp %st(0),%st(2)
+
+ jmp LSetSides
+
+//dist1= p->normal[0]*emaxs[0] + p->normal[1]*emins[1] + p->normal[2]*emaxs[2];
+//dist2= p->normal[0]*emins[0] + p->normal[1]*emaxs[1] + p->normal[2]*emins[2];
+Lcase2:
+ fmuls (%ebx) // emaxs[0]
+ flds pl_normal+4(%edx)
+ fxch %st(2)
+ fmuls (%ecx) // emins[0]
+ fxch %st(2)
+ fld %st(0)
+ fmuls 4(%ecx) // emins[1]
+ flds pl_normal+8(%edx)
+ fxch %st(2)
+ fmuls 4(%ebx) // emaxs[1]
+ fxch %st(2)
+ fld %st(0)
+ fmuls 8(%ebx) // emaxs[2]
+ fxch %st(5)
+ faddp %st(0),%st(3)
+ fmuls 8(%ecx) // emins[2]
+ fxch %st(1)
+ faddp %st(0),%st(3)
+ fxch %st(3)
+ faddp %st(0),%st(2)
+
+ jmp LSetSides
+
+//dist1= p->normal[0]*emins[0] + p->normal[1]*emins[1] + p->normal[2]*emaxs[2];
+//dist2= p->normal[0]*emaxs[0] + p->normal[1]*emaxs[1] + p->normal[2]*emins[2];
+Lcase3:
+ fmuls (%ecx) // emins[0]
+ flds pl_normal+4(%edx)
+ fxch %st(2)
+ fmuls (%ebx) // emaxs[0]
+ fxch %st(2)
+ fld %st(0)
+ fmuls 4(%ecx) // emins[1]
+ flds pl_normal+8(%edx)
+ fxch %st(2)
+ fmuls 4(%ebx) // emaxs[1]
+ fxch %st(2)
+ fld %st(0)
+ fmuls 8(%ebx) // emaxs[2]
+ fxch %st(5)
+ faddp %st(0),%st(3)
+ fmuls 8(%ecx) // emins[2]
+ fxch %st(1)
+ faddp %st(0),%st(3)
+ fxch %st(3)
+ faddp %st(0),%st(2)
+
+ jmp LSetSides
+
+//dist1= p->normal[0]*emaxs[0] + p->normal[1]*emaxs[1] + p->normal[2]*emins[2];
+//dist2= p->normal[0]*emins[0] + p->normal[1]*emins[1] + p->normal[2]*emaxs[2];
+Lcase4:
+ fmuls (%ebx) // emaxs[0]
+ flds pl_normal+4(%edx)
+ fxch %st(2)
+ fmuls (%ecx) // emins[0]
+ fxch %st(2)
+ fld %st(0)
+ fmuls 4(%ebx) // emaxs[1]
+ flds pl_normal+8(%edx)
+ fxch %st(2)
+ fmuls 4(%ecx) // emins[1]
+ fxch %st(2)
+ fld %st(0)
+ fmuls 8(%ecx) // emins[2]
+ fxch %st(5)
+ faddp %st(0),%st(3)
+ fmuls 8(%ebx) // emaxs[2]
+ fxch %st(1)
+ faddp %st(0),%st(3)
+ fxch %st(3)
+ faddp %st(0),%st(2)
+
+ jmp LSetSides
+
+//dist1= p->normal[0]*emins[0] + p->normal[1]*emaxs[1] + p->normal[2]*emins[2];
+//dist2= p->normal[0]*emaxs[0] + p->normal[1]*emins[1] + p->normal[2]*emaxs[2];
+Lcase5:
+ fmuls (%ecx) // emins[0]
+ flds pl_normal+4(%edx)
+ fxch %st(2)
+ fmuls (%ebx) // emaxs[0]
+ fxch %st(2)
+ fld %st(0)
+ fmuls 4(%ebx) // emaxs[1]
+ flds pl_normal+8(%edx)
+ fxch %st(2)
+ fmuls 4(%ecx) // emins[1]
+ fxch %st(2)
+ fld %st(0)
+ fmuls 8(%ecx) // emins[2]
+ fxch %st(5)
+ faddp %st(0),%st(3)
+ fmuls 8(%ebx) // emaxs[2]
+ fxch %st(1)
+ faddp %st(0),%st(3)
+ fxch %st(3)
+ faddp %st(0),%st(2)
+
+ jmp LSetSides
+
+//dist1= p->normal[0]*emaxs[0] + p->normal[1]*emins[1] + p->normal[2]*emins[2];
+//dist2= p->normal[0]*emins[0] + p->normal[1]*emaxs[1] + p->normal[2]*emaxs[2];
+Lcase6:
+ fmuls (%ebx) // emaxs[0]
+ flds pl_normal+4(%edx)
+ fxch %st(2)
+ fmuls (%ecx) // emins[0]
+ fxch %st(2)
+ fld %st(0)
+ fmuls 4(%ecx) // emins[1]
+ flds pl_normal+8(%edx)
+ fxch %st(2)
+ fmuls 4(%ebx) // emaxs[1]
+ fxch %st(2)
+ fld %st(0)
+ fmuls 8(%ecx) // emins[2]
+ fxch %st(5)
+ faddp %st(0),%st(3)
+ fmuls 8(%ebx) // emaxs[2]
+ fxch %st(1)
+ faddp %st(0),%st(3)
+ fxch %st(3)
+ faddp %st(0),%st(2)
+
+ jmp LSetSides
+
+//dist1= p->normal[0]*emins[0] + p->normal[1]*emins[1] + p->normal[2]*emins[2];
+//dist2= p->normal[0]*emaxs[0] + p->normal[1]*emaxs[1] + p->normal[2]*emaxs[2];
+Lcase7:
+ fmuls (%ecx) // emins[0]
+ flds pl_normal+4(%edx)
+ fxch %st(2)
+ fmuls (%ebx) // emaxs[0]
+ fxch %st(2)
+ fld %st(0)
+ fmuls 4(%ecx) // emins[1]
+ flds pl_normal+8(%edx)
+ fxch %st(2)
+ fmuls 4(%ebx) // emaxs[1]
+ fxch %st(2)
+ fld %st(0)
+ fmuls 8(%ecx) // emins[2]
+ fxch %st(5)
+ faddp %st(0),%st(3)
+ fmuls 8(%ebx) // emaxs[2]
+ fxch %st(1)
+ faddp %st(0),%st(3)
+ fxch %st(3)
+ faddp %st(0),%st(2)
+
+LSetSides:
+
+// sides = 0;
+// if (dist1 >= p->dist)
+// sides = 1;
+// if (dist2 < p->dist)
+// sides |= 2;
+
+ faddp %st(0),%st(2) // dist1 | dist2
+ fcomps pl_dist(%edx)
+ xorl %ecx,%ecx
+ fnstsw %ax
+ fcomps pl_dist(%edx)
+ andb $1,%ah
+ xorb $1,%ah
+ addb %ah,%cl
+
+ fnstsw %ax
+ andb $1,%ah
+ addb %ah,%ah
+ addb %ah,%cl
+
+// return sides;
+
+ popl %ebx
+ movl %ecx,%eax // return status
+
+ ret
+
+
+Lerror:
+ movl 1, %eax
+ ret
+
+#endif // id386
diff --git a/code/unix/pcons-2.3.1 b/code/unix/pcons-2.3.1
new file mode 100755
index 0000000..db9452c
--- /dev/null
+++ b/code/unix/pcons-2.3.1
@@ -0,0 +1,7911 @@
+#!/usr/bin/env perl
+
+#
+# Revision history:
+# -----------------
+# July 2001, Thomas Gleerup <tgl@oticon.dk>
+# 1) pcons-1.6 (John Erickson, August 1999) merged into cons-2.3.0.
+# 2) Added automatic insertion of && for multi-line commands.
+#
+# September 2001, Thomas Gleerup <tgl@oticon.dk>
+# 1) Updated with Kevin Nolish's multi-line improvement.
+# 2) Improved this so that single [perl] commands can still be used.
+#
+
+# NOTE: Cons intentionally does not use the "perl -w" option or
+# "use strict." Because Cons "configuration files" are actually
+# Perl scripts, enabling those restrictions here would force them
+# on every user's config files, wanted or not. Would users write
+# "better" Construct and Conscript files if we forced "use strict"
+# on them? Probably. But we want people to use Cons to get work
+# done, not force everyone to become a Perl guru to use it, so we
+# don't insist.
+#
+# That said, Cons' code is both "perl -w" and "use strict" clean.
+# Regression tests keep the code honest by checking for warnings
+# and "use strict" failures.
+
+use vars qw( $CVS_id $CVS_ver $ver_num $ver_rev $version );
+
+# I hate those CVS tags
+$CVS_id = 'pcons-2.3.1';
+$CVS_ver = (split (/\s+/, $CVS_id))[2];
+
+$ver_num = "__VERSION__";
+$ver_rev = "__REVISION__";
+
+#$version = "This is Cons $ver_num$ver_rev ($CVS_id)\n";
+$version = "This is parallel Cons (pcons) $ver_num$ver_rev ($CVS_id)\n";
+
+# Cons: A Software Construction Tool.
+# Copyright (c) 1996-2001 Free Software Foundation, Inc.
+#
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 2 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; see the file COPYING. If not, write to
+# the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+# Boston, MA 02111-1307, USA.
+
+require 5.003;
+
+# See the NOTE above about why Cons doesn't "use strict".
+use integer;
+use Cwd;
+use File::Copy;
+
+use vars qw( $_WIN32 $_a $_exe $_o $_so );
+
+#------------------------------------------------------------------
+# Determine if running on win32 platform - either Windows NT or 95
+#------------------------------------------------------------------
+
+use vars qw( $PATH_SEPARATOR $iswin32 $_WIN32 $usage $indent @targets );
+
+BEGIN
+{
+ use Config;
+
+ # if the version is 5.003, we can check $^O
+ if ($] < 5.003)
+ {
+ eval("require Win32");
+ $_WIN32 = (!$@);
+ }
+ else
+ {
+ $_WIN32 = ($^O eq "MSWin32") ? 1 : 0;
+ }
+
+ # Fetch the PATH separator from Config;
+ # provide our old defaults in case it's not set.
+ $PATH_SEPARATOR = $Config{path_sep};
+ $PATH_SEPARATOR = $_WIN32 ? ';' : ':' if !defined $PATH_SEPARATOR;
+
+ # Fetch file suffixes from Config,
+ # accomodating differences in the Config variables
+ # used by different Perl versions.
+ $_exe = $Config{_exe};
+ $_exe = $Config{exe_ext} if !defined $_exe;
+ $_exe = $_WIN32 ? '.exe' : '' if !defined $_exe;
+ $_o = $Config{_o};
+ $_o = $Config{obj_ext} if !defined $_o;
+ $_o = $_WIN32 ? '.obj' : '.o' if !defined $_o;
+ $_a = $Config{_a};
+ $_a = $Config{lib_ext} if !defined $_a;
+ $_a = $_WIN32 ? '.lib' : '.a' if !defined $_a;
+ $_so = ".$Config{so}";
+ $_so = $_WIN32 ? '.dll' : '.so' if !defined $_so;
+}
+
+# Flush stdout each time.
+$| = 1;
+
+# Seed random number generator.
+srand(time . $$); # this works better than time ^ $$ in perlfunc manpage.
+
+$usage = q(
+Usage: cons <arguments> -- <construct-args>
+
+Arguments can be any of the following, in any order:
+
+ <targets> Build the specified targets. If <target> is a directory
+ recursively build everything within that directory.
+
+ +<pattern> Limit the cons scripts considered to just those that
+ match <pattern>. Multiple + arguments are accepted.
+
+ <name>=<val> Sets <name> to value <val> in the ARG hash passed to the
+ top-level Construct file.
+
+ -cc Show command that would have been executed, when
+ retrieving from cache. No indication that the file
+ has been retrieved is given; this is useful for
+ generating build logs that can be compared with
+ real build logs.
+
+ -cd Disable all caching. Do not retrieve from cache nor
+ flush to cache.
+
+ -cr Build dependencies in random order. This is useful when
+ building multiple similar trees with caching enabled.
+
+ -cs Synchronize existing build targets that are found to be
+ up-to-date with cache. This is useful if caching has
+ been disabled with -cc or just recently enabled with
+ UseCache.
+
+ -d Enable dependency debugging.
+
+ -f <file> Use the specified file instead of "Construct" (but first
+ change to containing directory of <file>).
+
+ -h Show a help message local to the current build if
+ one such is defined, and exit.
+
+ -k Keep going as far as possible after errors.
+
+ -o <file> Read override file <file>.
+
+ -p Show construction products in specified trees.
+ -pa Show construction products and associated actions.
+ -pw Show products and where they are defined.
+
+ -q Be quiet; multiple -q flags increase quietness level:
+ 1: quiet about Installing and Removing targets
+ 2: quiet about build commands, up-to-date targets
+
+ -r Remove construction products associated with <targets>
+
+ -R <repos> Search for files in <repos>. Multiple -R <repos>
+ directories are searched in the order specified.
+
+ -S <pkg> Use package sig::<pkg> to calculate file signatures.
+ Currently supported values are "md5" for MD5
+ signatures (the default) and "md5::debug" for MD5
+ signature debug information.
+
+ -t Traverse up the directory hierarchy looking for a
+ Construct file, if none exists in the current directory.
+ (Targets will be modified to be relative to the
+ Construct file.)
+
+ -v Show cons version and continue processing.
+ -V Show cons version and exit.
+
+ -wf <file> Write all filenames considered into <file>.
+
+ -x Show this message and exit.
+
+
+ Please report any suggestions through the cons-discuss@gnu.org mailing
+ list.
+
+ To subscribe, send mail to cons-discuss-request@gnu.org with body
+ 'subscribe'.
+
+ If you find a bug, please report it through the bug-cons@gnu.org
+ mailing list.
+
+ Information about CONS can be obtained from the official cons web site
+ http://www.dsmit.com/cons/ or its mirrors (listed there).
+
+ The cons maintainers can be contacted by email at cons-maintainers@gnu.org
+
+ User documentation of cons is contained in cons and can be obtained
+ by doing 'perldoc /path/to/cons'.
+
+);
+my $pcons = 1;
+
+# Simplify program name, if it is a path.
+{
+ my ($vol, $dir, $file) = File::Spec->splitpath(File::Spec->canonpath($0));
+ $0 = $file;
+}
+
+# Default parameters.
+$param::topfile = 'Construct'; # Top-level construction file.
+$param::install = 1; # Show installations
+$param::build = 1; # Build targets
+ ### $param::show = 1; # Show building of targets.
+$param::sigpro = 'md5'; # Signature protocol.
+$param::depfile = ''; # Write all deps out to this file
+$param::salt = ''; # Salt derived file signatures with this.
+$param::sourcesig = ['*' => 'content']; # Source file signature calculation
+$param::rep_sig_times_ok = 1; # Repository .consign times are in sync
+ # w/files.
+$param::conscript_chdir = 0; # Change dir to Conscript directory
+$param::quiet = 0; # should we show the command being executed.
+$param::max_jobs = 1; # pcons
+
+@param::defaults = ();
+
+#
+$indent = '';
+
+# Display a command while executing or otherwise. This
+# should be called by command builder action methods.
+sub showcom
+{
+ print($indent . $_[0] . "\n") if ($param::quiet < 2);
+}
+
+# Default environment.
+# This contains only the completely platform-independent information
+# we can figure out. Platform-specific information (UNIX, Win32)
+# gets added below.
+@param::base = (
+ 'SIGNATURE' => ['*' => 'build'],
+ 'SUFEXE' => $_exe, # '' on UNIX systems
+ 'SUFLIB' => $_a, # '.a' on UNIX systems
+ 'SUFLIBS' => "$_so:$_a", # '.so:.a' on UNIX
+ 'SUFOBJ' => $_o, # '.o' on UNIX systems
+ 'SUFMAP' => {
+ '.c' => 'build::command::cc',
+ '.s' => 'build::command::cc',
+ '.S' => 'build::command::cc',
+ '.C' => 'build::command::cxx',
+ '.cc' => 'build::command::cxx',
+ '.cxx' => 'build::command::cxx',
+ '.cpp' => 'build::command::cxx',
+ '.c++' => 'build::command::cxx',
+ '.C++' => 'build::command::cxx',
+ },
+ 'PERL' => $^X,
+ );
+
+# pcons does not allow multi-line commands
+my $ar_command = ($param::max_jobs <= 1)
+ ? # pcons
+ ['%AR %ARFLAGS %> %<', '%RANLIB %>']
+ : # cons
+ '%AR %ARFLAGS %> %< && %RANLIB %>'; # pcons
+
+%param::rulesets = (
+
+ # Defaults for Win32.
+ # Defined for VC++ 6.0 by Greg Spencer <greg_spencer@acm.org>
+ # Your mileage may vary.
+ 'msvc' => [
+ 'CC' => 'cl',
+ 'CFLAGS' => '/nologo',
+ 'CCCOM' => '%CC %CFLAGS %_IFLAGS /c %< /Fo%>',
+ 'CXX' => '%CC',
+ 'CXXFLAGS' => '%CFLAGS',
+ 'CXXCOM' => '%CXX %CXXFLAGS %_IFLAGS /c %< /Fo%>',
+ 'INCDIRPREFIX' => '/I',
+ 'INCDIRSUFFIX' => '',
+ 'LINK' => 'link',
+ 'LINKCOM' => '%LINK %LDFLAGS /out:%> %< %_LDIRS %LIBS',
+ 'LINKMODULECOM' => '%LD /r /o %> %<',
+ 'LIBDIRPREFIX' => '/LIBPATH:',
+ 'LIBDIRSUFFIX' => '',
+ 'AR' => 'lib',
+ 'ARFLAGS' => '/nologo ',
+ 'ARCOM' => "%AR %ARFLAGS /out:%> %<",
+ 'RANLIB' => '',
+ 'LD' => 'link',
+ 'LDFLAGS' => '/nologo ',
+ 'PREFLIB' => '',
+ ],
+
+ # Defaults for a typical (?) UNIX platform.
+ # Your mileage may vary.
+ 'unix' => [
+ 'CC' => 'cc',
+ 'CFLAGS' => '',
+ 'CCCOM' => '%CC %CFLAGS %_IFLAGS -c %< -o %>',
+ 'CXX' => '%CC',
+ 'CXXFLAGS' => '%CFLAGS',
+ 'CXXCOM' => '%CXX %CXXFLAGS %_IFLAGS -c %< -o %>',
+ 'INCDIRPREFIX' => '-I',
+ 'INCDIRSUFFIX' => '',
+ 'LINK' => '%CXX',
+ 'LINKCOM' => '%LINK %LDFLAGS -o %> %< %_LDIRS %LIBS',
+ 'LINKMODULECOM' => '%LD -r -o %> %<',
+ 'LIBDIRPREFIX' => '-L',
+ 'LIBDIRSUFFIX' => '',
+ 'AR' => 'ar',
+ 'ARFLAGS' => 'r', # rs?
+ 'ARCOM' => $ar_command, # pcons
+ 'RANLIB' => 'ranlib',
+ 'AS' => 'as',
+ 'ASFLAGS' => '',
+ 'ASCOM' => '%AS %ASFLAGS %< -o %>',
+ 'LD' => 'ld',
+ 'LDFLAGS' => '',
+ 'PREFLIB' => 'lib',
+ 'ENV' => {'PATH' => '/bin:/usr/bin'},
+ ],
+ );
+
+# Set the rules based on the platform.
+script::DefaultRules(script::RuleSet($_WIN32 ? 'msvc' : 'unix'));
+
+# Handle command line arguments.
+while (@ARGV)
+{
+ $_ = shift @ARGV;
+ last if /^--$/; # Argument passing to Construct.
+ &option, next if s/^-//;
+ push (@param::include, $_), next if s/^\+//;
+ &equate, next if /=/;
+ push (@targets, $_), next;
+}
+
+sub option
+{
+ my %opt = (
+ 'cc' => sub { $param::cachecom = 1; },
+ 'cd' => sub { $param::cachedisable = 1; },
+ 'cr' => sub { $param::random = 1; },
+ 'cs' => sub { $param::cachesync = 1; },
+ 'd' => sub { $param::depends = 1; },
+ 'h' => sub { $param::localhelp = 1; },
+ 'k' => sub { $param::kflag = 1; },
+ 'p' => sub {
+ $param::pflag = 1;
+ $param::build = 0;
+ },
+ 'pa' => sub {
+ $param::pflag = 1;
+ $param::aflag = 1;
+ $indent = "... ";
+ $param::build = 0;
+ },
+ 'pw' => sub {
+ $param::pflag = 1;
+ $param::wflag = 1;
+ $param::build = 0;
+ },
+ 'q' => sub { $param::quiet++; },
+ 'r' => sub {
+ $param::rflag = 1;
+ $param::build = 0;
+ },
+ 't' => sub { $param::traverse = 1; },
+ 'v' => sub { print($version); },
+ 'V' => sub { print($version), exit(0); },
+ 'x' => sub { print($usage), exit 0; },
+ );
+
+ my %opt_arg = (
+ 'f' => sub { $param::topfile = $_[0]; },
+ 'o' => sub { $param::overfile = $_[0]; },
+ 'R' => sub { script::Repository($_[0]); },
+ 'S' => sub { $param::sigpro = $_[0]; },
+ 'wf' => sub { $param::depfile = $_[0]; },
+ 'j' => sub { $param::max_jobs = $_[0]; }, # pcons
+ );
+
+ if (defined $opt{$_})
+ {
+ &{$opt{$_}} ();
+ return;
+ }
+ while ($_)
+ {
+ $_ =~ m/(.)(.*)/;
+ if (defined $opt{$1})
+ {
+ &{$opt{$1}} ();
+ $_ = $2;
+ next;
+ }
+ if (defined $opt_arg{$1})
+ {
+ if (!$2)
+ {
+ $_ = shift @ARGV;
+ die ("$0: -$1 option requires an argument.\n") if !$_;
+ }
+ &{$opt_arg{$1}} ($2 || $_);
+ return;
+ }
+ $_ =~ m/(..)(.*)/;
+ if (defined $opt_arg{$1})
+ {
+ if (!$2)
+ {
+ $_ = shift @ARGV;
+ die ("$0: -$1 option requires an argument.\n") if !$_;
+ }
+ &{$opt_arg{$1}} ($2 || $_);
+ return;
+ }
+ if ($_)
+ {
+ die
+ qq($0: unrecognized option "-$_". Use -x for a usage message.\n);
+ }
+ }
+}
+
+# Process an equate argument (var=val).
+sub equate
+{
+ my ($var, $val) = /([^=]*)=(.*)/;
+ $script::ARG{$var} = $val;
+}
+
+# Define file signature protocol.
+'sig'->select($param::sigpro);
+
+# Cleanup after an interrupt.
+$SIG{INT} = $SIG{QUIT} = $SIG{TERM} = sub {
+ $SIG{PIPE} = $SIG{INT} = $SIG{QUIT} = $SIG{TERM} = 'IGNORE';
+ $SIG{HUP} = $SIG{INT} if !$main::_WIN32;
+ warn("\n$0: killed\n");
+
+ # Call this first, to make sure that this processing
+ # occurs even if a child process does not die (and we
+ # hang on the wait).
+ sig::hash::END();
+ wait();
+ exit(1);
+};
+$SIG{HUP} = $SIG{INT} if !$main::_WIN32;
+
+# Cleanup after a broken pipe (someone piped our stdout?)
+$SIG{PIPE} = sub {
+ $SIG{PIPE} = $SIG{HUP} = $SIG{INT} = $SIG{QUIT} = $SIG{TERM} = 'IGNORE';
+ warn("\n$0: broken pipe\n");
+ sig::hash::END();
+ wait();
+ exit(1);
+};
+
+if ($param::depfile)
+{
+ open(main::DEPFILE, ">" . $param::depfile)
+ || die ("$0: couldn't open $param::depfile ($!)\n");
+}
+
+# If the supplied top-level Conscript file is not in the
+# current directory, then change to that directory.
+{
+ my ($vol, $dir, $file) =
+ File::Spec->splitpath(File::Spec->canonpath($param::topfile));
+ if ($vol || $dir)
+ {
+ my ($cd) = File::Spec->catpath($vol, $dir, undef);
+ chdir($cd) || die ("$0: couldn't change to directory $cd ($!)\n");
+ $param::topfile = $file;
+ }
+}
+
+# Walk up the directory hierarchy looking for a Conscript file (if -t set).
+my ($target_top);
+my (@targetdir) = ();
+if ($param::traverse && !-f $param::topfile)
+{
+ my ($vol, $dirs, $file) = File::Spec->splitpath(cwd());
+ my (@dirs) = (File::Spec->splitdir($dirs), $file);
+ while (
+ !-f File::Spec->catpath($vol, File::Spec->catdir(@dirs),
+ $param::topfile))
+ {
+ die ("$0: unable to find $param::topfile.\n") if !@dirs;
+ unshift (@targetdir, pop (@dirs));
+ }
+ my ($cwd) = File::Spec->catpath($vol, File::Spec->catdir(@dirs), '');
+ print "$0: Entering directory `$cwd'\n";
+ chdir($cwd);
+ @targets = map { File::Spec->catdir(@targetdir, $_) } @targets;
+}
+
+# Set up $dir::top and $dir::cwd, now that we are in the right directory.
+dir::init();
+
+#
+if (@targetdir)
+{
+ $target_top = $dir::top->lookupdir(File::Spec->catdir(@targetdir));
+}
+
+# Now handle override file.
+package override;
+if ($param::overfile)
+{
+ my ($ov) = $param::overfile;
+ die qq($0: can\'t read override file "$ov" ($!)\n) if !-f $ov; #'
+ do $ov;
+ if ($@)
+ {
+ chop($@);
+ die qq($0: errors in override file "$ov" ($@)\n);
+ }
+}
+
+# Provide this to user to setup override patterns.
+sub Override
+{
+ my ($re, @env) = @_;
+ return if $param::overrides{$re}; # if identical, first will win.
+ $param::overrides = 1;
+ $param::overrides{$re} = \@env;
+ push (@param::overrides, $re);
+}
+
+package main;
+
+use vars qw( %priority $errors );
+
+# Check script inclusion regexps
+my $re;
+for $re (@param::include)
+{
+ if (!defined eval { "" =~ /$re/ })
+ {
+ my ($err) = $@;
+ $err =~ s/in regexp at .*$//;
+ die ("$0: error in regexp $err");
+ }
+}
+
+# Read the top-level construct file and its included scripts.
+doscripts($param::topfile);
+
+# Status priorities. This lets us aggregate status for directories
+# and print an appropriate message (at the top-level).
+%priority =
+ ('none' => 1, 'handled' => 2, 'built' => 3, 'unknown' => 4, 'errors' => 5);
+
+# If no targets were specified, supply default targets (if any).
+@targets = @param::default_targets if !@targets;
+
+$errors = 0;
+
+# Build the supplied target patterns.
+my $tgt;
+for $tgt (map($dir::top->lookup($_), @targets))
+{
+ if ($target_top && !$tgt->is_under($target_top))
+ {
+
+ # A -t option was used, and this target is not underneath
+ # the directory where we were invoked via -t.
+ # If the target is a directory and the -t directory
+ # is underneath it, then build the -t directory.
+ if (ref $tgt ne "dir" || !$target_top->is_under($tgt))
+ {
+ next;
+ }
+ $tgt = $target_top;
+ }
+ buildtoptarget($tgt);
+}
+
+exit 0 + ($errors != 0);
+
+sub buildtoptarget
+{
+ my ($tgt) = @_;
+ return if !$tgt;
+ my ($status) = buildtarget($tgt);
+ if ($status ne 'built')
+ {
+ my ($path) = $tgt->path;
+ if ($status eq "errors")
+ {
+ print qq($0: "$path" not remade because of errors.\n);
+ $errors++;
+ }
+ elsif ($status eq "handled")
+ {
+ print qq($0: "$path" is up-to-date.\n) if ($param::quiet < 2);
+ }
+ elsif ($status eq "unknown")
+ {
+
+ # cons error already reported.
+ $errors++;
+ }
+ elsif ($status eq "none")
+ {
+
+ # search for targets that may be linked to the given path.
+ my @linked = dir::linked_targets($tgt) if $target_top;
+ if (@linked)
+ {
+ my @names = map($_->path, @linked);
+ print "Linked targets: @names\n" if ($param::quiet < 1);
+ map(buildtoptarget($_), @linked);
+ }
+ else
+ {
+ print qq($0: nothing to be built in "$path".\n)
+ if $param::build && ($param::quiet < 2);
+ }
+ }
+ else
+ {
+ print qq($0: don\'t know how to construct "$path".\n); #'
+ $errors++;
+ }
+ }
+}
+
+# Build the supplied target directory or files. Return aggregated status.
+sub buildtarget
+{
+ my ($tgt) = @_;
+ if (ref($tgt) eq "dir")
+ {
+ my ($result) = "none";
+ my ($priority) = $priority{$result};
+ if (exists $tgt->{member})
+ {
+ my ($members) = $tgt->{member};
+ my $entry;
+ for $entry (sort keys %$members)
+ {
+ next if $entry eq $dir::CURDIR || $entry eq $dir::UPDIR;
+ my ($tgt) = $members->{$entry};
+ next if ref($tgt) ne "dir" && !exists($tgt->{builder});
+ my ($stat) = buildtarget($members->{$entry});
+ my ($pri) = $priority{$stat};
+ if ($pri > $priority)
+ {
+ $priority = $pri;
+ $result = $stat;
+ }
+ }
+ }
+ return $result;
+ }
+ if ($param::depends)
+ {
+ my ($path) = $tgt->path;
+ if ($tgt->{builder})
+ {
+ my (@dep) = (@{$tgt->{dep}}, @{$tgt->{sources}});
+ my ($dep) = join (' ', map($_->path, @dep));
+ print("Target $path: $dep\n");
+ }
+ else
+ {
+ print("Target $path: not a derived file\n");
+ }
+ }
+ if ($param::build)
+ {
+ return build $tgt;
+ }
+ elsif ($param::pflag || $param::wflag || $param::aflag)
+ {
+ if ($tgt->{builder})
+ {
+ if ($param::wflag)
+ {
+ print qq(${\$tgt->path}: $tgt->{script}\n);
+ }
+ elsif ($param::pflag)
+ {
+ print qq(${\$tgt->path}:\n) if $param::aflag;
+ print qq(${\$tgt->path}\n) if !$param::aflag;
+ }
+ if ($param::aflag)
+ {
+ $tgt->{builder}->action($tgt);
+ }
+ }
+ }
+ elsif ($param::rflag && $tgt->{builder})
+ {
+ my ($path) = $tgt->path;
+ if (-f $path)
+ {
+ if (unlink($path))
+ {
+ print("Removed $path\n") if ($param::quiet < 1);
+ }
+ else
+ {
+ warn("$0: couldn't remove $path\n");
+ }
+ }
+ }
+
+ return "none";
+}
+
+package NameSpace;
+
+# Return a hash that maps the name of symbols in a namespace to an
+# array of refs for all types for which the name has a defined value.
+# A list of symbols may be specified; default is all symbols in the
+# name space.
+sub save
+{
+ my $package = shift;
+ my (%namerefs, $var, $type);
+ no strict 'refs';
+ @_ = keys %{$package . "::"} if !@_;
+ foreach $var (@_)
+ {
+ $namerefs{$var} = [];
+ my $fqvar = $package . "::" . $var;
+
+ # If the scalar for this variable name doesn't already
+ # exist, *foo{SCALAR} will autovivify the reference
+ # instead of returning undef, so unlike the other types,
+ # we have to dereference to find out if it exists.
+ push (@{$namerefs{$var}}, *{$fqvar}{SCALAR})
+ if defined ${*{$fqvar}{SCALAR}};
+ foreach $type (qw(ARRAY HASH CODE IO))
+ {
+ push (@{$namerefs{$var}}, *{$fqvar}{$type})
+ if defined *{$fqvar}{$type};
+ }
+ }
+ return \%namerefs;
+}
+
+# Remove the specified symbols from the namespace.
+# Default is to remove all.
+sub remove
+{
+ my $package = shift;
+ my (%namerefs, $var);
+ no strict 'refs';
+ @_ = keys %{$package . "::"} if !@_;
+ foreach $var (@_)
+ {
+ delete ${$package . "::"}{$var};
+ }
+}
+
+# Restore values to symbols specified in a hash as returned
+# by NameSpace::save.
+sub restore
+{
+ my ($package, $namerefs) = @_;
+ my ($var, $ref);
+ no strict 'refs';
+ foreach $var (keys %$namerefs)
+ {
+ my $fqvar = $package . "::" . $var;
+ foreach $ref (@{$namerefs->{$var}})
+ {
+ *{$fqvar} = $ref;
+ }
+ }
+}
+
+# Support for "building" scripts, importing and exporting variables.
+# With the exception of the top-level routine here (invoked from the
+# main package by cons), these are all invoked by user scripts.
+package script;
+
+use vars qw( $ARG $caller_dir_path %special_var );
+
+BEGIN
+{
+
+ # We can't Export or Import the following variables because Perl always
+ # treats them as part of the "main::" package (see perlvar(1)).
+ %special_var = map { $_ => 1 } qw(ENV INC ARGV ARGVOUT SIG
+ STDIN STDOUT STDERR);
+}
+
+# This is called from main to interpret/run the top-level Construct
+# file, passed in as the single argument.
+sub main::doscripts
+{
+ my ($script) = @_;
+ Build($script);
+
+ # Now set up the includes/excludes (after the Construct file is read).
+ $param::include = join ('|', @param::include);
+
+ # Save the original variable names from the script package.
+ # These will stay intact, but any other "script::" variables
+ # defined in a Conscript file will get saved, deleted,
+ # and (when necessary) restored.
+ my (%orig_script_var) = map { $_ => 1 } keys %script::;
+ $caller_dir_path = undef;
+ my $cwd = Cwd::cwd();
+ my (@scripts) = pop (@priv::scripts);
+ while ($priv::self = shift (@scripts))
+ {
+ my ($path) = $priv::self->{script}->rsrcpath;
+ if (-f $path)
+ {
+ $dir::cwd = $priv::self->{script}->{dir};
+
+ # Handle chdir to the Conscript file directory, if necessary.
+ my ($vol, $dir, $file);
+ if ($param::conscript_chdir)
+ {
+ ($vol, $dir, $file) =
+ File::Spec->splitpath(File::Spec->canonpath($path));
+ if ($vol ne '' || $dir ne '')
+ {
+ $caller_dir_path = File::Spec->catpath($vol, $dir, undef);
+ chdir($caller_dir_path)
+ || die "Could not chdir to $caller_dir_path: $!\n";
+ }
+ }
+ else
+ {
+ $file = $path;
+ }
+
+ # Actually process the Conscript file.
+ do $file;
+
+ # Save any variables defined by the Conscript file
+ # so we can restore them later, if needed;
+ # then delete them from the script:: namespace.
+ my (@del) = grep(!$orig_script_var{$_}, keys %script::);
+ if (@del)
+ {
+ $priv::self->{script}->{pkgvars} =
+ NameSpace::save('script', @del);
+ NameSpace::remove('script', @del);
+ }
+ if ($caller_dir_path)
+ {
+ chdir($cwd);
+ $caller_dir_path = undef;
+ }
+ if ($@)
+ {
+ chomp($@);
+ my $err = ($@ =~ /\n/ms) ? ":\n$@" : " ($@)";
+ print qq($0: error in file "$path"$err\n);
+ $run::errors++;
+ }
+ else
+ {
+
+ # Only process subsidiary scripts if no errors in parent.
+ unshift (@scripts, @priv::scripts);
+ }
+ undef @priv::scripts;
+ }
+ else
+ {
+ my $where = '';
+ my $cref = $priv::self->{script}->creator;
+ if (defined $cref)
+ {
+ my ($_foo, $script, $line, $sub) = @$cref;
+ $where = " ($sub in $script, line $line)";
+ }
+ warn qq(Ignoring missing script "$path"$where);
+ }
+ }
+ die ("$0: script errors encountered: construction aborted\n")
+ if $run::errors;
+}
+
+# Return caller info about the method being invoked.
+# This is everything from the Perl "caller" builtin function,
+# including which Construct/Conscript file, line number,
+# subroutine name, etc.
+sub caller_info
+{
+ my ($lev) = 1;
+ my (@frame);
+ do
+ {
+ @frame = caller ++$lev;
+ if (defined($frame[3]) && $frame[3] eq '(eval)')
+ {
+ @frame = caller --$lev;
+ if ($caller_dir_path)
+ {
+ $frame[1] = File::Spec->catfile($caller_dir_path, $frame[1]);
+ }
+ return @frame;
+ }
+ } while ($frame[3]);
+ return;
+}
+
+# Link a directory to another. This simply means set up the *source*
+# for the directory to be the other directory.
+sub Link
+{
+ dir::link(@_);
+}
+
+# Add directories to the repository search path for files.
+# Strip our current directory from the list so Repository
+# (or -R options) can be used from within the repository.
+sub Repository
+{
+ my ($my_dir) = Cwd::cwd();
+ my $dir;
+ foreach $dir (@_)
+ {
+
+ # The following more direct call isn't available in
+ # Cwd.pm until some time after 5.003...
+ # my($d) = Cwd::abs_path($dir);
+ chdir($dir);
+ my ($d) = Cwd::cwd();
+ chdir($my_dir);
+
+ #
+ next if !$d || !-d $d || $d eq $my_dir;
+
+ # We know we can get away with passing undef to lookupdir
+ # as the directory because $dir is an absolute path.
+ push (@param::rpath, dir::lookupdir(undef, $dir));
+ push @INC, $d;
+ }
+}
+
+# Return the list of Repository directories specified.
+sub Repository_List
+{
+ map($_->path, @param::rpath);
+}
+
+# Specify whether the .consign signature times in repository files are,
+# in fact, consistent with the times on the files themselves.
+sub Repository_Sig_Times_OK
+{
+ $param::rep_sig_times_ok = shift;
+}
+
+sub SourceSignature
+{
+ $param::sourcesig = [@_];
+}
+
+# Specify whether we should chdir to the containing directories
+# of Conscript files.
+sub Conscript_chdir
+{
+ $param::conscript_chdir = shift;
+}
+
+# Specify files/targets that must be present and built locally,
+# even if they exist already-built in a Repository.
+sub Local
+{
+ my (@files) = map($dir::cwd->lookupfile($_), @_);
+ map($_->local(1), @files);
+}
+
+# Export variables to any scripts invoked from this one.
+sub Export
+{
+ my (@illegal) = grep($special_var{$_}, @_);
+ if (@illegal)
+ {
+ die qq($0: cannot Export special Perl variables: @illegal\n);
+ }
+ @{$priv::self->{exports}} = grep(!defined $special_var{$_}, @_);
+}
+
+# Import variables from the export list of the caller
+# of the current script.
+sub Import
+{
+ my (@illegal) = grep($special_var{$_}, @_);
+ if (@illegal)
+ {
+ die qq($0: cannot Import special Perl variables: @illegal\n);
+ }
+ my ($parent) = $priv::self->{parent};
+ my ($imports) = $priv::self->{imports};
+ @{$priv::self->{exports}} = keys %$imports;
+ my ($var);
+ foreach $var (grep(!defined $special_var{$_}, @_))
+ {
+ if (!exists $imports->{$var})
+ {
+ my ($path) = $parent->{script}->path;
+ die qq($0: variable "$var" not exported by file "$path"\n);
+ }
+ if (!defined $imports->{$var})
+ {
+ my $path = $parent->{script}->path;
+ my $err =
+ "$0: variable \"$var\" exported but not "
+ . "defined by file \"$path\"\n";
+ die $err;
+ }
+ ${"script::$var"} = $imports->{$var};
+ }
+}
+
+# Build an inferior script. That is, arrange to read and execute
+# the specified script, passing to it any exported variables from
+# the current script.
+sub Build
+{
+ my (@files) = map($dir::cwd->lookupfile($_), @_);
+ my (%imports) = map { $_ => ${"script::$_"} } @{$priv::self->{exports}};
+ my $file;
+ for $file (@files)
+ {
+ next if $param::include && $file->path !~ /$param::include/o;
+ my ($self) = {
+ 'script' => $file,
+ 'parent' => $priv::self,
+ 'imports' => \%imports
+ };
+ bless $self; # may want to bless into class of parent in future
+ push (@priv::scripts, $self);
+ }
+}
+
+# Set up regexps dependencies to ignore. Should only be called once.
+sub Ignore
+{
+ die ("Ignore called more than once\n") if $param::ignore;
+ $param::ignore = join ("|", map("($_)", @_)) if @_;
+}
+
+# Specification of default targets.
+sub Default
+{
+ push (@param::default_targets, map($dir::cwd->lookup($_)->path, @_));
+}
+
+# Local Help. Should only be called once.
+sub Help
+{
+ if ($param::localhelp)
+ {
+ print "@_\n";
+ exit 2;
+ }
+}
+
+# For windows platforms which use unix tool sets, the msvc defaults may
+# not be useful. Also, in the future, other platforms (Mac?) may have the
+# same problem.
+sub RuleSet
+{
+ my $style = shift;
+ my @rulesets = sort keys %param::rulesets;
+ die "Unknown style for rules: $style.\n"
+ . "Supported rules are: ("
+ . join (" ", @rulesets) . ")"
+ unless eval(join ("||", map("\$style eq '$_'", @rulesets)));
+ return @param::base, @{$param::rulesets{$style}};
+}
+
+sub DefaultRules
+{
+ @param::defaults = ();
+ push @param::defaults, @_;
+}
+
+# Return the build name(s) of a file or file list.
+sub FilePath
+{
+ wantarray
+ ? map($dir::cwd->lookupfile($_)->path, @_)
+ : $dir::cwd->lookupfile($_[0])->path;
+}
+
+# Return the build name(s) of a directory or directory list.
+sub DirPath
+{
+ wantarray
+ ? map($dir::cwd->lookupdir($_)->path, @_)
+ : $dir::cwd->lookupdir($_[0])->path;
+}
+
+# Split the search path provided into components. Look each up
+# relative to the current directory.
+# The usual path separator problems abound; for now we'll use :
+sub SplitPath
+{
+ my ($dirs) = @_;
+ if (ref($dirs) ne "ARRAY")
+ {
+ $dirs = [split (/$main::PATH_SEPARATOR/o, $dirs)];
+ }
+ map { DirPath($_) } @$dirs;
+}
+
+# Return true if the supplied path is available as a source file
+# or is buildable (by rules seen to-date in the build).
+sub ConsPath
+{
+ my ($path) = @_;
+ my ($file) = $dir::cwd->lookup($path);
+ return $file->accessible;
+}
+
+# Return the source path of the supplied path.
+sub SourcePath
+{
+ wantarray
+ ? map($dir::cwd->lookupfile($_)->rsrcpath, @_)
+ : $dir::cwd->lookupfile($_[0])->rsrcpath;
+}
+
+# Search up the tree for the specified cache directory, starting with
+# the current directory. Returns undef if not found, 1 otherwise.
+# If the directory is found, then caching is enabled. The directory
+# must be readable and writable. If the argument "mixtargets" is provided,
+# then targets may be mixed in the cache (two targets may share the same
+# cache file--not recommended).
+sub UseCache($@)
+{
+ my ($dir, @args) = @_;
+
+ # NOTE: it's important to process arguments here regardless of whether
+ # the cache is disabled temporarily, since the mixtargets option affects
+ # the salt for derived signatures.
+ for (@args)
+ {
+ if ($_ eq "mixtargets")
+ {
+
+ # When mixtargets is enabled, we salt the target signatures.
+ # This is done purely to avoid a scenario whereby if
+ # mixtargets is turned on or off after doing builds, and
+ # if cache synchronization with -cs is used, then
+ # cache files may be shared in the cache itself (linked
+ # under more than one name in the cache). This is not bad,
+ # per se, but simply would mean that a cache cleaning algorithm
+ # that looked for a link count of 1 would never find those
+ # particular files; they would always appear to be in use.
+ $param::salt = 'M' . $param::salt;
+ $param::mixtargets = 1;
+ }
+ else
+ {
+ die qq($0: UseCache unrecognized option "$_"\n);
+ }
+ }
+ if ($param::cachedisable)
+ {
+ warn("Note: caching disabled by -cd flag\n");
+ return 1;
+ }
+ my ($depth) = 15;
+ while ($depth-- && !-d $dir)
+ {
+ $dir = File::Spec->catdir($dir::UPDIR, $dir);
+ }
+ if (-d $dir)
+ {
+ $param::cache = $dir;
+ return 1;
+ }
+ return undef;
+}
+
+# Salt the signature generator. The salt (a number of string) is added
+# into the signature of each derived file. Changing the salt will
+# force recompilation of all derived files.
+sub Salt($)
+{
+
+ # We append the value, so that UseCache and Salt may be used
+ # in either order without changing the signature calculation.
+ $param::salt .= $_[0];
+}
+
+# Mark files (or directories) to not be removed before building.
+sub Precious
+{
+ map($_->{precious} = 1, map($dir::cwd->lookup($_), @_));
+}
+
+# These methods are callable from Conscript files, via a cons
+# object. Procs beginning with _ are intended for internal use.
+package cons;
+
+use vars qw( %envcache );
+
+# This is passed the name of the base environment to instantiate.
+# Overrides to the base environment may also be passed in
+# as key/value pairs.
+sub new
+{
+ my ($package) = shift;
+ my ($env) = {@param::defaults, @_};
+ @{$env->{_envcopy}} = %$env; # Note: we never change PATH
+ $env->{_cwd} = $dir::cwd; # Save directory of environment for
+ bless $env, $package; # any deferred name interpretation.
+}
+
+# Clone an environment.
+# Note that the working directory will be the initial directory
+# of the original environment.
+sub clone
+{
+ my ($env) = shift;
+ my $clone = {@{$env->{_envcopy}}, @_};
+ @{$clone->{_envcopy}} = %$clone; # Note: we never change PATH
+ $clone->{_cwd} = $env->{_cwd};
+ bless $clone, ref $env;
+}
+
+# Create a flattened hash representing the environment.
+# It also contains a copy of the PATH, so that the path
+# may be modified if it is converted back to a hash.
+sub copy
+{
+ my ($env) = shift;
+ (@{$env->{_envcopy}}, 'ENV' => {%{$env->{ENV}}}, @_);
+}
+
+# Resolve which environment to actually use for a given
+# target. This is just used for simple overrides.
+sub _resolve
+{
+ return $_[0] if !$param::overrides;
+ my ($env, $tgt) = @_;
+ my ($path) = $tgt->path;
+ my $re;
+ for $re (@param::overrides)
+ {
+ next if $path !~ /$re/;
+
+ # Found one. Return a combination of the original environment
+ # and the override.
+ my ($ovr) = $param::overrides{$re};
+ return $envcache{$env, $re} if $envcache{$env, $re};
+ my ($newenv) = {@{$env->{_envcopy}}, @$ovr};
+ @{$newenv->{_envcopy}} = %$env;
+ $newenv->{_cwd} = $env->{_cwd};
+ return $envcache{$env, $re} = bless $newenv, ref $env;
+ }
+ return $env;
+}
+
+# Substitute construction environment variables into a string.
+# Internal function/method.
+sub _subst
+{
+ my ($env, $str) = @_;
+ if (!defined $str)
+ {
+ return undef;
+ }
+ elsif (ref($str) eq "ARRAY")
+ {
+ return [map($env->_subst($_), @$str)];
+ }
+ else
+ {
+
+ # % expansion. %% gets converted to % later, so expand any
+ # %keyword construction that doesn't have a % in front of it,
+ # modulo multiple %% pairs in between.
+ # In Perl 5.005 and later, we could actually do this in one regex
+ # using a conditional expression as follows,
+ # while ($str =~ s/($pre)\%(\{)?([_a-zA-Z]\w*)(?(2)\})/"$1".
+ # $env->{$3}/ge) {}
+ # The following two-step approach is backwards-compatible
+ # to (at least) Perl5.003.
+ my $pre = '^|[^\%](?:\%\%)*';
+ while (($str =~ s/($pre)\%([_a-zA-Z]\w*)/$1.($env->{$2}||'')/ge)
+ || ($str =~ s/($pre)\%\{([_a-zA-Z]\w*)\}/$1.($env->{$2}||'')/ge))
+ {
+ }
+ return $str;
+ }
+}
+
+sub AfterBuild
+{
+ my ($env) = shift;
+ my ($perl_eval_str) = pop (@_);
+ my $file;
+ for $file (map($dir::cwd->lookup($_), @_))
+ {
+ $file->{after_build_func} = $perl_eval_str;
+
+ }
+}
+
+sub Install
+{
+ my ($env) = shift;
+ my ($tgtdir) = $dir::cwd->lookupdir($env->_subst(shift));
+ my $file;
+ for $file (map($dir::cwd->lookupfile($env->_subst($_)), @_))
+ {
+ my ($tgt) = $tgtdir->lookupfile($file->{entry});
+ $tgt->bind(find build::install($env), $file);
+ }
+}
+
+sub InstallAs
+{
+ my $env = shift;
+ my $tgt = shift;
+ my $src = shift;
+ my @sources = ();
+ my @targets = ();
+
+ if (ref $tgt)
+ {
+ die "InstallAs: Source is a file and target is a list!\n"
+ if (!ref($src));
+ @sources = @$src;
+ @targets = @$tgt;
+ }
+ elsif (ref $src)
+ {
+ die "InstallAs: Target is a file and source is a list!\n";
+ }
+ else
+ {
+ push @sources, $src;
+ push @targets, $tgt;
+ }
+
+ if ($#sources != $#targets)
+ {
+ my $tn = $#targets + 1;
+ my $sn = $#sources + 1;
+ die "InstallAs: Source file list ($sn) and target file list ($tn) "
+ . "are inconsistent in length!\n";
+ }
+ else
+ {
+ foreach (0 .. $#sources)
+ {
+ my $tfile = $dir::cwd->lookupfile($env->_subst($targets[$_]));
+ my $sfile = $dir::cwd->lookupfile($env->_subst($sources[$_]));
+ $tfile->bind(find build::install($env), $sfile);
+ }
+ }
+}
+
+# Installation in a local build directory,
+# copying from the repository if it's already built there.
+# Functionally equivalent to:
+# Install $env $dir, $file;
+# Local "$dir/$file";
+sub Install_Local
+{
+ my ($env) = shift;
+ my ($tgtdir) = $dir::cwd->lookupdir($env->_subst(shift));
+ my $file;
+ for $file (map($dir::cwd->lookupfile($env->_subst($_)), @_))
+ {
+ my ($tgt) = $tgtdir->lookupfile($file->{entry});
+ $tgt->bind(find build::install($env), $file);
+ $tgt->local(1);
+ }
+}
+
+sub Objects
+{
+ my ($env) = shift;
+ map($dir::cwd->relpath($_), $env->_Objects(@_));
+}
+
+# Called with multiple source file references (or object files).
+# Returns corresponding object files references.
+sub _Objects
+{
+ my ($env) = shift;
+ my ($suffix) = $env->{SUFOBJ};
+ map($env->_Object($_, $_->{dir}->lookupfile($_->base_suf($suffix))),
+ map { ref $_ ? $_ : $dir::cwd->lookupfile($env->_subst($_)) }
+ grep(defined $_, @_));
+}
+
+# Called with an object and source reference. If no object reference
+# is supplied, then the object file is determined implicitly from the
+# source file's extension. Sets up the appropriate rules for creating
+# the object from the source. Returns the object reference.
+sub _Object
+{
+ my ($env, $src, $obj) = @_;
+ return $obj if $src eq $obj; # don't need to build self from self.
+ my ($objenv) = $env->_resolve($obj);
+ my ($suffix) = $src->suffix;
+
+ my ($builder) = $env->{SUFMAP}{$suffix};
+
+ if ($builder)
+ {
+ $obj->bind((find $builder($objenv)), $src);
+ }
+ else
+ {
+ die ("don't know how to construct ${\$obj->path} from "
+ . "${\$src->path}.\n");
+ }
+ $obj;
+}
+
+sub Program
+{
+ my ($env) = shift;
+ my ($tgt) =
+ $dir::cwd->lookupfile(
+ file::addsuffix($env->_subst(shift), $env->{SUFEXE}));
+ my ($progenv) = $env->_resolve($tgt);
+ $tgt->bind(find build::command::link($progenv, $progenv->{LINKCOM}),
+ $env->_Objects(@_));
+}
+
+sub Module
+{
+ my ($env) = shift;
+ my ($tgt) = $dir::cwd->lookupfile($env->_subst(shift));
+ my ($modenv) = $env->_resolve($tgt);
+ my ($com) = pop (@_);
+ $tgt->bind(find build::command::link($modenv, $com), $env->_Objects(@_));
+}
+
+sub LinkedModule
+{
+ my ($env) = shift;
+ my ($tgt) = $dir::cwd->lookupfile($env->_subst(shift));
+ my ($progenv) = $env->_resolve($tgt);
+ $tgt->bind(
+ find build::command::linkedmodule($progenv,
+ $progenv->{LINKMODULECOM}),
+ $env->_Objects(@_));
+}
+
+sub Library
+{
+ my ($env) = shift;
+ my ($lib) =
+ $dir::cwd->lookupfile(
+ file::addsuffix($env->_subst(shift), $env->{SUFLIB}));
+ my ($libenv) = $env->_resolve($lib);
+ $lib->bind(find build::command::library($libenv), $env->_Objects(@_));
+}
+
+# Simple derivation: you provide target, source(s), command.
+# Special variables substitute into the rule.
+# Target may be a reference, in which case it is taken
+# to be a multiple target (all targets built at once).
+sub Command
+{
+ my ($env) = shift;
+ my ($tgt) = $env->_subst(shift);
+ my ($builder) = find build::command::user($env, pop (@_), 'script');
+ my (@sources) = map($dir::cwd->lookupfile($env->_subst($_)), @_);
+ if (ref($tgt))
+ {
+
+ # A multi-target command.
+ my (@tgts) = map($dir::cwd->lookupfile($_), @$tgt);
+ die ("empty target list in multi-target command\n") if !@tgts;
+ $env = $env->_resolve($tgts[0]);
+ my ($multi) = build::multiple->new($builder, \@tgts);
+ for $tgt (@tgts)
+ {
+ $tgt->bind($multi, @sources);
+ }
+ }
+ else
+ {
+ $tgt = $dir::cwd->lookupfile($tgt);
+ $env = $env->_resolve($tgt);
+ $tgt->bind($builder, @sources);
+ }
+}
+
+sub Depends
+{
+ my ($env) = shift;
+ my ($tgt) = $env->_subst(shift);
+ my (@deps) = map($dir::cwd->lookup($env->_subst($_)), @_);
+ if (!ref($tgt))
+ {
+ $tgt = [$tgt];
+ }
+ my ($t);
+ foreach $t (map($dir::cwd->lookupfile($_), @$tgt))
+ {
+ push (@{$t->{dep}}, @deps);
+ }
+}
+
+# Setup a quick scanner for the specified input file, for the
+# associated environment. Any use of the input file will cause the
+# scanner to be invoked, once only. The scanner sees just one line at
+# a time of the file, and is expected to return a list of
+# dependencies.
+sub QuickScan
+{
+ my ($env, $code, $file, $path) = @_;
+ $dir::cwd->lookup($env->_subst($file))->{'srcscan', $env} =
+ find scan::quickscan($code, $env, $env->_subst($path));
+}
+
+# Generic builder module. Just a few default methods. Every derivable
+# file must have a builder object of some sort attached. Usually
+# builder objects are shared.
+package build;
+
+use vars qw( %builder );
+
+# Every builder must now have at least an associated environment,
+# so we can find its sigarray and calculate the proper signature.
+sub find
+{
+ my ($class, $env) = @_;
+ $builder{$env} || do
+ {
+ my $self = {env => $env};
+ $builder{$env} = bless $self, $class;
+ }
+}
+
+# Null signature for dynamic includes.
+sub includes { () }
+
+# Null signature for build script.
+sub scriptsig { () }
+
+# Not compatible with any other builder, by default.
+sub compatible { 0 }
+
+# Builder module for the Install command.
+package build::install;
+
+use vars qw( @ISA );
+
+BEGIN { @ISA = qw(build) }
+
+# Caching not supported for Install: generally install is trivial anyway,
+# and we don't want to clutter the cache.
+sub cachin { undef }
+sub cachout { }
+
+# Do the installation.
+sub action
+{
+ my ($self, $tgt) = @_;
+ my ($src) = $tgt->{sources}[0];
+ main::showcom("Install ${\$src->rpath} as ${\$tgt->path}")
+ if ($param::install && $param::quiet < 1);
+ return unless $param::build;
+ futil::install($src->rpath, $tgt);
+ return 1;
+}
+
+# Builder module for generic UNIX commands.
+package build::command;
+
+use vars qw( @ISA %com );
+
+BEGIN { @ISA = qw(build) }
+
+sub find
+{
+ my ($class, $env, $cmd, $package) = @_;
+ my ($act) = action::new($env, $cmd);
+ $package ||= '';
+ $com{$env, $act, $package} || do
+ {
+ my $self = {env => $env, act => $act, 'package' => $package};
+ $com{$env, $act, $package} = bless $self, $class;
+ }
+}
+
+# Default cache in function.
+sub cachin
+{
+ my ($self, $tgt, $sig) = @_;
+ if (cache::in($tgt, $sig))
+ {
+ if ($param::cachecom)
+ {
+ $self->{act}->show($self->{env}, $tgt);
+ }
+ else
+ {
+ printf("Retrieved %s from cache\n", $tgt->path)
+ if ($param::quiet < 1);
+ }
+ return 1;
+ }
+ return undef;
+}
+
+# Default cache out function.
+sub cachout
+{
+ my ($self, $tgt, $sig) = @_;
+ cache::out($tgt, $sig);
+}
+
+# Build the target using the previously specified commands.
+sub action
+{
+ my ($self, $tgt) = @_;
+ $self->{act}->execute($self->{env}, $tgt, $self->{'package'});
+}
+
+# Return script signature.
+sub scriptsig
+{
+ $_[0]->{act}->scriptsig;
+}
+
+# Create a linked module.
+package build::command::link;
+
+use vars qw( @ISA );
+
+BEGIN { @ISA = qw(build::command) }
+
+# Find an appropriate linker.
+sub find
+{
+ my ($class, $env, $command) = @_;
+ if (!exists $env->{_LDIRS})
+ {
+ my ($ldirs) = '';
+ my ($wd) = $env->{_cwd};
+ my ($pdirs) = $env->{LIBPATH};
+ if (!defined $pdirs)
+ {
+ $pdirs = [];
+ }
+ elsif (ref($pdirs) ne 'ARRAY')
+ {
+ $pdirs = [split (/$main::PATH_SEPARATOR/o, $pdirs)];
+ }
+ my ($dir, $dpath);
+ for $dir (map($wd->lookupdir($env->_subst($_)), @$pdirs))
+ {
+ $dpath = $dir->path;
+
+ # Add the (presumably local) directory to the -L flags
+ # if we're not using repositories, the directory exists,
+ # or it's Linked to a source directory (that is, it *will*
+ # exist by the time the link occurs).
+ $ldirs .= " " . $env->{LIBDIRPREFIX} . $dpath . $env->{LIBDIRSUFFIX}
+ if !@param::rpath || -d $dpath || $dir->is_linked;
+ next if File::Spec->file_name_is_absolute($dpath);
+ if (@param::rpath)
+ {
+ my $d;
+ if ($dpath eq $dir::CURDIR)
+ {
+ foreach $d (map($_->path, @param::rpath))
+ {
+ $ldirs .= " "
+ . $env->{LIBDIRPREFIX} . $d
+ . $env->{LIBDIRSUFFIX};
+ }
+ }
+ else
+ {
+ my ($rpath);
+ foreach $d (map($_->path, @param::rpath))
+ {
+ $rpath = File::Spec->catfile($d, $dpath);
+ $ldirs .= " "
+ . $env->{LIBDIRPREFIX} . $rpath
+ . $env->{LIBDIRSUFFIX}
+ if -d $rpath;
+ }
+ }
+ }
+ }
+ $env->{_LDIRS} = "%($ldirs%)";
+ }
+
+ # Introduce a new magic _LIBS symbol which allows to use the
+ # Unix-style -lNAME syntax for Win32 only. -lNAME will be replaced
+ # with %{PREFLIB}NAME%{SUFLIB}. <schwarze@isa.de> 1998-06-18
+
+ if ($main::_WIN32 && !exists $env->{_LIBS})
+ {
+ my $libs;
+ my $name;
+ for $name (split (' ', $env->_subst($env->{LIBS} || '')))
+ {
+ if ($name =~ /^-l(.*)/)
+ {
+ $name = "$env->{PREFLIB}$1$env->{SUFLIB}";
+ }
+ $libs .= ' ' . $name;
+ }
+ $env->{_LIBS} = $libs ? "%($libs%)" : '';
+ }
+ bless find build::command($env, $command);
+}
+
+# Called from file::build. Make sure any libraries needed by the
+# environment are built, and return the collected signatures
+# of the libraries in the path.
+sub includes
+{
+ return $_[0]->{'bsig'} if exists $_[0]->{'bsig'};
+ my ($self, $tgt) = @_;
+ my ($env) = $self->{env};
+ my ($ewd) = $env->{_cwd};
+ my $ldirs = $env->{LIBPATH};
+ if (!defined $ldirs)
+ {
+ $ldirs = [];
+ }
+ elsif (ref($ldirs) ne 'ARRAY')
+ {
+ $ldirs = [split (/$main::PATH_SEPARATOR/o, $ldirs)];
+ }
+ my @lpath = map($ewd->lookupdir($_), @$ldirs);
+ my (@sigs);
+ my (@names);
+
+ # Pass %LIBS symbol through %-substituition
+ # <schwarze@isa.de> 1998-06-18
+ @names = split (' ', $env->_subst($env->{LIBS} || ''));
+ my $name;
+ for $name (@names)
+ {
+ my ($lpath, @allnames);
+ if ($name =~ /^-l(.*)/)
+ {
+
+ # -l style names are looked up on LIBPATH, using all
+ # possible lib suffixes in the same search order the
+ # linker uses (according to SUFLIBS).
+ # Recognize new PREFLIB symbol, which should be 'lib' on
+ # Unix, and empty on Win32. TODO: What about shared
+ # library suffixes? <schwarze@isa.de> 1998-05-13
+ @allnames =
+ map("$env->{PREFLIB}$1$_", split (/:/, $env->{SUFLIBS}));
+ $lpath = \@lpath;
+ }
+ else
+ {
+ @allnames = ($name);
+
+ # On Win32, all library names are looked up in LIBPATH
+ # <schwarze@isa.de> 1998-05-13
+ if ($main::_WIN32)
+ {
+ $lpath = [$dir::top, @lpath];
+ }
+ else
+ {
+ $lpath = [$dir::top];
+ }
+ }
+ my $dir;
+ DIR: for $dir (@$lpath)
+ {
+ my $n;
+ for $n (@allnames)
+ {
+ my ($lib) = $dir->lookup_accessible($n);
+ if ($lib)
+ {
+ last DIR if $lib->ignore;
+ if ((build $lib) eq 'errors')
+ {
+ $tgt->{status} = 'errors';
+ return undef;
+ }
+ push (@sigs, 'sig'->signature($lib));
+ last DIR;
+ }
+ }
+ }
+ }
+ $self->{'bsig'} = 'sig'->collect(@sigs);
+}
+
+# Always compatible with other such builders, so the user
+# can define a single program or module from multiple places.
+sub compatible
+{
+ my ($self, $other) = @_;
+ ref($other) eq "build::command::link";
+}
+
+# Link a program.
+package build::command::linkedmodule;
+
+use vars qw( @ISA );
+
+BEGIN { @ISA = qw(build::command) }
+
+# Always compatible with other such builders, so the user
+# can define a single linked module from multiple places.
+sub compatible
+{
+ my ($self, $other) = @_;
+ ref($other) eq "build::command::linkedmodule";
+}
+
+# Builder for a C module
+package build::command::cc;
+
+use vars qw( @ISA );
+
+BEGIN { @ISA = qw(build::command) }
+
+sub find
+{
+ $_[1]->{_cc} || do
+ {
+ my ($class, $env) = @_;
+ my ($cpppath) = $env->_subst($env->{CPPPATH});
+ my ($cscanner) = find scan::cpp($env->{_cwd}, $cpppath);
+ $env->{_IFLAGS} = "%(" . $cscanner->iflags($env) . "%)";
+ my ($self) = find build::command($env, $env->{CCCOM});
+ $self->{scanner} = $cscanner;
+ bless $env->{_cc} = $self;
+ }
+}
+
+# Invoke the associated C scanner to get signature of included files.
+sub includes
+{
+ my ($self, $tgt) = @_;
+ $self->{scanner}->includes($tgt, $tgt->{sources}[0]);
+}
+
+# Builder for a C++ module
+package build::command::cxx;
+
+use vars qw( @ISA );
+
+BEGIN { @ISA = qw(build::command) }
+
+sub find
+{
+ $_[1]->{_cxx} || do
+ {
+ my ($class, $env) = @_;
+ my ($cpppath) = $env->_subst($env->{CPPPATH});
+ my ($cscanner) = find scan::cpp($env->{_cwd}, $cpppath);
+ $env->{_IFLAGS} = "%(" . $cscanner->iflags($env) . "%)";
+ my ($self) = find build::command($env, $env->{CXXCOM});
+ $self->{scanner} = $cscanner;
+ bless $env->{_cxx} = $self;
+ }
+}
+
+# Invoke the associated C scanner to get signature of included files.
+sub includes
+{
+ my ($self, $tgt) = @_;
+ $self->{scanner}->includes($tgt, $tgt->{sources}[0]);
+}
+
+# Builder for a user command (cons::Command). We assume that a user
+# command might be built and implement the appropriate dependencies on
+# the command itself (actually, just on the first word of the command
+# line).
+package build::command::user;
+
+use vars qw( @ISA );
+
+BEGIN { @ISA = qw(build::command) }
+
+sub includes
+{
+ my ($self, $tgt) = @_;
+ my ($sig) = '';
+
+ # Check for any quick scanners attached to source files.
+ my $dep;
+ for $dep (@{$tgt->{dep}}, @{$tgt->{sources}})
+ {
+ my ($scanner) = $dep->{'srcscan', $self->{env}};
+ if ($scanner)
+ {
+ $sig .= $scanner->includes($tgt, $dep);
+ }
+ }
+
+ # XXX Optimize this to not use ignored paths.
+ if (!exists $self->{_comsig})
+ {
+ my ($env) = $self->{env};
+ $self->{_comsig} = '';
+ my ($com, $dir);
+ com:
+ for $com ($self->{act}->commands)
+ {
+ my ($pdirs) = $env->{ENV}->{PATH};
+ if (!defined $pdirs)
+ {
+ $pdirs = [];
+ }
+ elsif (ref($pdirs) ne 'ARRAY')
+ {
+ $pdirs = [split (/$main::PATH_SEPARATOR/o, $pdirs)];
+ }
+ for $dir (map($dir::top->lookupdir($_), @$pdirs))
+ {
+ my ($prog) = $dir->lookup_accessible($com);
+ if ($prog)
+ { # XXX Not checking execute permission.
+ if ((build $prog) eq 'errors')
+ {
+ $tgt->{status} = 'errors';
+ return $sig;
+ }
+ next com if $prog->ignore;
+ $self->{_comsig} .= 'sig'->signature($prog);
+ next com;
+ }
+ }
+ }
+ }
+
+ return $self->{_comsig} . $sig;
+}
+
+# Builder for a library module (archive).
+# We assume that a user command might be built and implement the
+# appropriate dependencies on the command itself.
+package build::command::library;
+
+use vars qw( @ISA );
+
+BEGIN { @ISA = qw(build::command) }
+
+sub find
+{
+ my ($class, $env) = @_;
+ bless find build::command($env, $env->{ARCOM});
+}
+
+# Always compatible with other library builders, so the user
+# can define a single library from multiple places.
+sub compatible
+{
+ my ($self, $other) = @_;
+ ref($other) eq "build::command::library";
+}
+
+# A multi-target builder.
+# This allows multiple targets to be associated with a single build
+# script, without forcing all the code to be aware of multiple targets.
+package build::multiple;
+
+sub new
+{
+ my ($class, $builder, $tgts) = @_;
+ bless {'builder' => $builder, 'env' => $builder->{env}, 'tgts' => $tgts};
+}
+
+sub scriptsig
+{
+ my ($self, $tgt) = @_;
+ $self->{builder}->scriptsig($tgt);
+}
+
+sub includes
+{
+ my ($self, $tgt) = @_;
+ $self->{builder}->includes($tgt);
+}
+
+sub compatible
+{
+ my ($self, $tgt) = @_;
+ $self->{builder}->compatible($tgt);
+}
+
+sub cachin
+{
+ my ($self, $tgt, $sig) = @_;
+ $self->{builder}->cachin($tgt, $sig);
+}
+
+sub cachout
+{
+ my ($self, $tgt, $sig) = @_;
+ $self->{builder}->cachout($tgt, $sig);
+}
+
+sub action
+{
+ my ($self, $invoked_tgt) = @_;
+ return $self->{built} if exists $self->{built};
+
+ # Make sure all targets in the group are unlinked before building any.
+ my ($tgts) = $self->{tgts};
+ my $tgt;
+ for $tgt (@$tgts)
+ {
+ futil::mkdir($tgt->{dir});
+ unlink($tgt->path) if !$tgt->precious;
+ }
+
+ # Now do the action to build all the targets. For consistency
+ # we always call the action on the first target, just so that
+ # $> is deterministic.
+ if ($param::max_jobs <= 1)
+ { # pcons
+ $self->{built} = $self->{builder}->action($tgts->[0]);
+ }
+ else
+ {
+ {
+
+ # action now is non-blocking, so we must kludge blocking for this
+ # explicit call
+ local ($file::child_queue) = {parent => $tgts->[0]}; # pcons
+ $self->{built} = $self->{builder}->action($tgts->[0]); # pcons
+ &file::wait_on_all_children(); # pcons
+ }
+ }
+
+ # Now "build" all the other targets (except for the one
+ # we were called with). This guarantees that the signature
+ # of each target is updated appropriately. We force the
+ # targets to be built even if they have been previously
+ # considered and found to be OK; the only effect this
+ # has is to make sure that signature files are updated
+ # correctly.
+ for $tgt (@$tgts)
+ {
+ if ($tgt ne $invoked_tgt)
+ {
+ delete $tgt->{status};
+ 'sig'->invalidate($tgt);
+ build $tgt;
+ }
+ }
+
+ # Status of action.
+ $self->{built};
+}
+
+package action;
+
+sub new
+{
+ my ($env, $act) = @_;
+ if (ref($act) eq 'CODE')
+ {
+ return action::perl->new($act);
+ }
+ else
+ {
+ return action::command->new($env, $act);
+ }
+}
+
+package action::command;
+
+use vars qw( @ISA %cmd %_varopts $_varletters );
+
+BEGIN
+{
+ @ISA = $main::_WIN32 ? 'action::command::win32' : 'action::command::unix';
+
+ # Internal hash for processing variable options.
+ # f: return file part
+ # d: return directory part
+ # F: return file part, but strip any suffix
+ # b: return full path, but strip any suffix (a.k.a. return basename)
+ # s: return only the suffix (or an empty string, if no suffix is there)
+ # a: return the absolute path to the file
+ # S: return the absolute path to a Linked source file
+ %_varopts = (
+ 'f' => sub { return $_[0]->{entry}; },
+ 'd' => sub { return $_[0]->{dir}->path; },
+ 'F' => sub {
+ my $subst = $_[0]->{entry};
+ $subst =~ s/\.[^\.]+$//;
+ return $subst;
+ },
+ 'b' => sub {
+ my $subst = $_[0]->path;
+ $subst =~ s/\.[^\.]+$//;
+ return $subst;
+ },
+ 's' => sub {
+ my $subst = $_[0]->{entry};
+ $subst =~ m/(\.[^\.]+)$/;
+ return $1;
+ },
+ 'a' => sub {
+ my $path = $_[0]->path;
+ if (!File::Spec->file_name_is_absolute($path))
+ {
+ $path = File::Spec->catfile(Cwd::cwd(), $path);
+ }
+ return $path;
+ },
+ 'S' => sub {
+ my $path = $_[0]->srcpath;
+ if (!File::Spec->file_name_is_absolute($path))
+ {
+ my $cwd = File::Spec->canonpath(Cwd::cwd());
+ $path = File::Spec->catfile($cwd, $path);
+ }
+ return $path;
+ },
+ );
+
+ $_varletters = join ('', keys %_varopts);
+}
+
+# Internal routine for processing variable options.
+# Options are specified in hash in the BEGIN block above.
+# no option: return path to file (relative to top,
+# or absolute if it's outside)
+sub _variant
+{
+ my ($opt, $file) = @_;
+ $opt = '' if !defined $opt;
+ if (defined $_varopts{$opt})
+ {
+ return &{$_varopts{$opt}} ($file);
+ }
+ return $file->path;
+}
+
+sub new
+{
+ my ($class, $env, $cmd) = @_;
+ $cmd = $env->_subst($cmd);
+ $cmd{$env, $cmd} || do
+ {
+
+ # Remove unwanted bits from signature -- those bracketed by %( ... %)
+ my $sigs = $cmd;
+ my $sig = '';
+ if (ref($sigs) eq 'ARRAY')
+ {
+
+ # This is an array of commands..
+ my $f;
+ foreach $f (@$sigs)
+ {
+ $sig .= _strip($f);
+ }
+ }
+ else
+ {
+ $sig = _strip($sigs);
+ }
+ my $self = {cmd => $cmd, cmdsig => 'sig'->cmdsig($sig)};
+ $cmd{$env, $cmd} = bless $self, $class;
+ };
+}
+
+sub _strip
+{
+ my $sig = shift;
+ $sig =~ s/^\@\s*//mg;
+ while ($sig =~ s/%\(([^%]|%[^\(])*?%\)//g) { }
+ $sig;
+}
+
+sub scriptsig
+{
+ $_[0]->{cmdsig};
+}
+
+# Return an array of all the commands (first word on each line).
+sub commands
+{
+ my ($self) = @_;
+ my (@cmds) = ();
+ my $com;
+ my $cmd = $self->{'cmd'};
+ my @allcoms;
+
+ push @allcoms, ref $cmd ? @{$cmd} : split (/\n/, $cmd);
+
+ for $com (@allcoms)
+ {
+ $com =~ s/^\s*//;
+ $com =~ s/\s.*//;
+ next if !$com; # blank line
+ push @cmds, $com;
+ }
+ @cmds;
+}
+
+# For the signature of a basic command, we don't bother
+# including the command itself. This is not strictly correct,
+# and if we wanted to be rigorous, we might want to insist
+# that the command was checked for all the basic commands
+# like gcc, etc. For this reason we don't have an includes
+# method.
+
+# Call this to get the command line script: an array of
+# fully substituted commands.
+sub getcoms
+{
+ my ($self, $env, $tgt) = @_;
+ my (@coms);
+ my $com;
+ my @allcoms = ();
+ my $cmd = $self->{'cmd'};
+
+ push @allcoms, ref $cmd ? @{$cmd} : split (/\n/, $cmd);
+
+ for $com (@allcoms)
+ {
+ my (@src) = (undef, @{$tgt->{sources}});
+ my (@src1) = @src;
+
+ next if $com =~ /^\s*$/;
+
+ # NOTE: we used to have a more elegant s//.../e solution
+ # for the items below, but this caused a bus error...
+
+ # Remove %( and %) -- those are only used to bracket parts
+ # of the command that we don't depend on.
+ $com =~ s/%[()]//g;
+
+ # Deal with %n, n=1,9 and variants.
+ while ($com =~ /%([1-9])(:([$_varletters]?))?/o)
+ {
+ my ($match) = $&;
+ my ($src) = $src1[$1];
+ my ($subst) = _variant($3, $src1[$1]->rfile);
+ undef $src[$1];
+ $com =~ s/$match/$subst/;
+ }
+
+ # Deal with %0 aka %> and variants.
+ while ($com =~ /%[0>](:([$_varletters]?))?/o)
+ {
+ my ($match) = $&;
+ my ($subst) = _variant($2, $tgt);
+ $com =~ s/$match/$subst/;
+ }
+
+ # Deal with %< (all sources except %n's already used)
+ while ($com =~ /%<(:([$_varletters]?))?/o)
+ {
+ my ($match) = $&;
+ my @list = ();
+ foreach (@src)
+ {
+ push (@list, _variant($2, $_->rfile)) if $_;
+ }
+ my ($subst) = join (' ', @list);
+ $com =~ s/$match/$subst/;
+ }
+
+ # Deal with %[ %].
+ $com =~ s{%\[(.*?)%\]}{
+ my($func, @args) = grep { $_ ne '' } split(/\s+/, $1);
+ die("$0: \"$func\" is not defined.\n")
+ unless ($env->{$func});
+ &{$env->{$func}}(@args);
+ }gex;
+
+ # Convert left-over %% into %.
+ $com =~ s/%%/%/g;
+
+ # White space cleanup. XXX NO WAY FOR USER TO HAVE QUOTED SPACES
+ $com = join (' ', split (' ', $com));
+ next if $com =~ /^:/ && $com !~ /^:\S/;
+ push (@coms, $com);
+ }
+ @coms;
+}
+
+# Build the target using the previously specified commands.
+sub execute
+{
+ my ($self, $env, $tgt, $package) = @_;
+
+ if ($param::build)
+ {
+ futil::mkdir($tgt->{dir});
+ unlink($tgt->path) if !$tgt->precious;
+ }
+
+ # Set environment.
+ map(delete $ENV{$_}, keys %ENV);
+ %ENV = %{$env->{ENV}};
+
+ # Handle multi-line commands.
+
+ my @cmds = $self->getcoms($env, $tgt); # pcons
+ if ($param::max_jobs > 1)
+ { # pcons
+ if ($#cmds > 0)
+ {
+ for ($i = 0 ; $i < @cmds ; $i++)
+ { #pcons -kn
+ $cmds[$i] = "( " . $cmds[$i] . " )"; #pcons -kn
+ } #pcons -kn
+ @cmds = join (" && ", @cmds); # pcons
+ } # pcons
+ } # pcons
+
+ my $com; # pcons
+ for $com (@cmds)
+ { # pcons
+ if ($com !~ s/^\@\s*//)
+ {
+ main::showcom($com);
+ }
+ next if !$param::build;
+
+ if ($com =~ /^\[perl\]\s*/)
+ {
+ my $perlcmd = $';
+ my $status;
+ {
+
+ # Restore the script package variables that were defined
+ # in the Conscript file that defined this [perl] build,
+ # so the code executes with the expected variables.
+ # Then actually execute (eval) the [perl] command to build
+ # the target, followed by cleaning up the name space
+ # by deleting the package variables we just restored.
+ my ($pkgvars) = $tgt->{conscript}->{pkgvars};
+ NameSpace::restore($package, $pkgvars) if $pkgvars;
+ $status = eval "package $package; $perlcmd";
+ NameSpace::remove($package, keys %$pkgvars) if $pkgvars;
+ }
+ if (!defined($status))
+ {
+ warn "$0: *** Error during perl command eval: $@.\n";
+ return undef;
+ }
+ elsif ($status == 0)
+ {
+ warn "$0: *** Perl command returned $status "
+ . "(this indicates an error).\n";
+ return undef;
+ }
+ next;
+ }
+ if (!$self->do_command($com, $tgt->path, $tgt))
+ {
+ return undef;
+ }
+ }
+
+ # success.
+ return 1;
+}
+
+sub show
+{
+ my ($self, $env, $tgt) = @_;
+ my $com;
+ for $com ($self->getcoms($env, $tgt))
+ {
+ if ($com !~ /^\@\s*/)
+ {
+ main::showcom($com);
+ }
+ }
+}
+
+package action::command::unix;
+
+sub do_command
+{
+ my ($class, $com, $path) = @_; # cons
+ my ($class, $com, $path, $tgt) = @_; # pcons
+
+ if ($param::max_jobs > 1)
+ { # pcons
+ &file::wait_on_max_jobs(); # pcons
+ } # pcons
+ my ($pid) = fork();
+ die ("$0: unable to fork child process ($!)\n") if !defined $pid;
+ if (!$pid)
+ {
+
+ # This is the child. We eval the command to suppress -w
+ # warnings about not reaching the statements afterwards.
+ eval 'exec($com)';
+ $com =~ s/\s.*//;
+ die qq($0: failed to execute "$com" ($!). )
+ . qq(Is this an executable on path "$ENV{PATH}"?\n);
+ }
+ if ($param::max_jobs <= 1)
+ { # pcons
+ for (; ;)
+ {
+ do { } until wait() == $pid;
+ my ($b0, $b1) = ($? & 0xFF, $? >> 8);
+
+ # Don't actually see 0177 on stopped process; is this necessary?
+ next if $b0 == 0177; # process stopped; we can wait.
+ if ($b0)
+ {
+ my ($core, $sig) = ($b0 & 0200, $b0 & 0177);
+ my ($coremsg) = $core ? "; core dumped" : "";
+ $com =~ s/\s.*//;
+ my $err =
+ "$0: *** \[$path\] $com terminated by signal "
+ . "$sig$coremsg\n";
+ warn $err;
+ return undef;
+ }
+ if ($b1)
+ {
+ warn qq($0: *** [$path] Error $b1\n); # trying to be like make.
+ return undef;
+ }
+ last;
+ }
+ }
+ else
+ { # pcons
+ $file::child_queue->{$pid}->{com} = $com; # pcons
+ $file::child_queue->{$pid}->{tgt} = $tgt; # pcons
+
+ } # pcons
+
+ return 1;
+}
+
+package action::command::win32;
+
+sub do_command
+{
+ my ($class, $com, $path, $tgt) = @_;
+ system($com);
+ if ($?)
+ {
+ my ($b0, $b1) = ($? & 0xFF, $? >> 8);
+ my $err = $b1 || $?;
+ my $warn = qq($0: *** [$path] Error $err);
+ $warn .= " (executable not found in path?)" if $b1 == 0xFF;
+ warn "$warn\n";
+ return undef;
+ }
+ return 1;
+}
+
+package action::perl;
+
+# THIS IS AN EXPERIMENTAL PACKAGE. It's entirely possible that the
+# interface may change as this gets completed, so use at your own risk.
+#
+# There are (at least) two issues that need to be solved before blessing
+# this as a real, fully-supported feature:
+#
+# -- We need to calculate a signature value for a Perl code ref, in
+# order to rebuild the target if there's a change to the Perl code
+# used to generate it.
+#
+# This is not straightforward. A B::Deparse package exists that
+# decompiles a coderef into text. It's reportedly not completely
+# reliable for closures; it misses which variables are global, and
+# the values of private lexicals. Nevertheless, it'd probably
+# be perfect for our purposes, except that it wasn't added until
+# some time between Perl 5.00502 and 5.00554, and doesn't seem to
+# really work until Perl 5.6.0, so by relying on it, we'd lose
+# support for Perl versions back to 5.003*.
+#
+# -- Ideally, a code ref should be able to use something like
+# $env->_subst to fetch values from the construction environment
+# to modify its behavior without having to cut-and-paste code.
+# (Actually, since we pass the environment to the executed code
+# ref, there's no reason you can't do this with the code as it
+# stands today.) But this REALLY complicates the signature
+# calculation, because now the actual signature would depend not
+# just on the code contents, but on the construction variables (or
+# maybe just the environment).
+#
+# A potentially valid workaround would be to use the contents of the
+# Conscript file in which the code reference is defined as the code
+# ref's signature. This has the drawback of causing a recompilation of
+# the target file even in response to unrelated changes in the Conscript
+# file, but it would ensure correct builds without having to solve the
+# messy issues of generating a signature directly from a code ref.
+#
+# Nevertheless, this seemed a useful enough skeleton of a feature that
+# it made sense to release it in hopes that some practical experience
+# will encourage someone to figure out how to solve the signature
+# issues. Or maybe we'll discover these aren't big issues in practice
+# and end up blessing it as is.
+
+use vars qw( %code );
+
+sub new
+{
+ my ($class, $cref) = @_;
+ $code{$cref} || do
+ {
+ my $sig = '';
+
+ # Generating a code signature using B::Deparse doesn't really
+ # work for us until Perl 5.6.0. Here's the code in case
+ # someone wants to use it.
+ #use B::Deparse;
+ #my $deparse = B::Deparse->new();
+ #my $body = $deparse->coderef2text($cref);
+ #$sig = $body; # should be an MD5 sig
+ my ($self) = {cref => $cref, crefsig => $sig};
+ $code{$cref} = bless $self, $class;
+ }
+}
+
+sub scriptsig
+{
+ $_[0]->{crefsig};
+}
+
+sub execute
+{
+ my ($self, $env, $tgt) = @_;
+ if ($param::build)
+ {
+ futil::mkdir($tgt->{dir});
+ unlink($tgt->path) if !$tgt->precious;
+ my ($cref) = $self->{cref};
+ &$cref($env, $tgt->path, map($_->rpath, @{$tgt->{sources}}));
+ }
+}
+
+sub commands
+{
+ return ();
+}
+
+# Generic scanning module.
+package scan;
+
+# Returns the signature of files included by the specified files on
+# behalf of the associated target. Any errors in handling the included
+# files are propagated to the target on whose behalf this processing
+# is being done. Signatures are cached for each unique file/scanner
+# pair.
+sub includes
+{
+ my ($self, $tgt, @files) = @_;
+ my (%files, $file);
+ my ($inc) = $self->{includes} || ($self->{includes} = {});
+ while ($file = pop @files)
+ {
+ next if exists $files{$file};
+ if ($inc->{$file})
+ {
+ push (@files, @{$inc->{$file}});
+ $files{$file} = 'sig'->signature($file->rfile);
+ }
+ else
+ {
+ if ((build $file) eq 'errors')
+ {
+ $tgt->{status} = 'errors'; # tgt inherits build status
+ return ();
+ }
+ $files{$file} = 'sig'->signature($file->rfile);
+ my (@includes) = $self->scan($file);
+ $inc->{$file} = \@includes;
+ push (@files, @includes);
+ }
+ }
+ 'sig'->collect(sort values %files);
+}
+
+# A simple scanner. This is used by the QuickScanfunction, to setup
+# one-time target and environment-independent scanning for a source
+# file. Only used for commands run by the Command method.
+package scan::quickscan;
+
+use vars qw( @ISA %scanner );
+
+BEGIN { @ISA = qw(scan) }
+
+sub find
+{
+ my ($class, $code, $env, $pdirs) = @_;
+ if (!defined $pdirs)
+ {
+ $pdirs = [];
+ }
+ elsif (ref($pdirs) ne 'ARRAY')
+ {
+ $pdirs = [split (/$main::PATH_SEPARATOR/o, $pdirs)];
+ }
+ my (@path) = map { $dir::cwd->lookupdir($_) } @$pdirs;
+ my ($spath) = "@path";
+ $scanner{$code, $env, $spath} || do
+ {
+ my ($self) = {code => $code, env => $env, path => \@path};
+ $scanner{$code, $env, $spath} = bless $self;
+ }
+}
+
+# Scan the specified file for included file names.
+sub scan
+{
+ my ($self, $file) = @_;
+ my ($code) = $self->{code};
+ my (@includes);
+
+ # File should have been built by now. If not, we'll ignore it.
+ return () unless open(SCAN, $file->rpath);
+ while (<SCAN>)
+ {
+ push (@includes, grep($_ ne '', &$code));
+ }
+ close(SCAN);
+ my ($wd) = $file->{dir};
+ my (@files);
+ my $name;
+ for $name (@includes)
+ {
+ my $dir;
+ for $dir ($file->{dir}, @{$self->{path}})
+ {
+ my ($include) = $dir->lookup_accessible($name);
+ if ($include)
+ {
+ push (@files, $include) unless $include->ignore;
+ last;
+ }
+ }
+ }
+ @files;
+}
+
+# CPP (C preprocessor) scanning module
+package scan::cpp;
+
+use vars qw( @ISA %scanner );
+
+BEGIN { @ISA = qw(scan) }
+
+# For this constructor, provide the include path argument (colon
+# separated). Each path is taken relative to the provided directory.
+
+# Note: a particular scanning object is assumed to always return the
+# same result for the same input. This is why the search path is a
+# parameter to the constructor for a CPP scanning object. We go to
+# some pains to make sure that we return the same scanner object
+# for the same path: otherwise we will unecessarily scan files.
+sub find
+{
+ my ($class, $dir, $pdirs) = @_;
+ if (!defined $pdirs)
+ {
+ $pdirs = [];
+ }
+ elsif (ref($pdirs) ne 'ARRAY')
+ {
+ $pdirs = [split (/$main::PATH_SEPARATOR/o, $pdirs)];
+ }
+ my @path = map($dir->lookupdir($_), @$pdirs);
+ my ($spath) = "@path";
+ $scanner{$spath} || do
+ {
+ my ($self) = {'path' => \@path};
+ $scanner{$spath} = bless $self;
+ }
+}
+
+# Scan the specified file for include lines.
+sub scan
+{
+ my ($self, $file) = @_;
+ my ($angles, $quotes);
+
+ if (exists $file->{angles})
+ {
+ $angles = $file->{angles};
+ $quotes = $file->{quotes};
+ }
+ else
+ {
+ my (@anglenames, @quotenames);
+ return () unless open(SCAN, $file->rpath);
+ while (<SCAN>)
+ {
+ next unless /^\s*#/;
+ if (/^\s*#\s*include\s*([<"])(.*?)[>"]/)
+ {
+ if ($1 eq "<")
+ {
+ push (@anglenames, $2);
+ }
+ else
+ {
+ push (@quotenames, $2);
+ }
+ }
+ }
+ close(SCAN);
+ $angles = $file->{angles} = \@anglenames;
+ $quotes = $file->{quotes} = \@quotenames;
+ }
+
+ my (@shortpath) = @{$self->{path}}; # path for <> style includes
+ my (@longpath) = ($file->{dir}, @shortpath); # path for "" style includes
+
+ my (@includes);
+
+ my $name;
+ for $name (@$angles)
+ {
+ my $dir;
+ for $dir (@shortpath)
+ {
+ my ($include) = $dir->lookup_accessible($name);
+ if ($include)
+ {
+ push (@includes, $include) unless $include->ignore;
+ last;
+ }
+ }
+ }
+
+ for $name (@$quotes)
+ {
+ my $dir;
+ for $dir (@longpath)
+ {
+ my ($include) = $dir->lookup_accessible($name);
+ if ($include)
+ {
+ push (@includes, $include) unless $include->ignore;
+ last;
+ }
+ }
+ }
+
+ return @includes;
+}
+
+# Return the include flags that would be used for a C Compile.
+sub iflags
+{
+ my ($self, $env) = @_;
+ my ($iflags) = '';
+ my ($dir, $dpath);
+ for $dir (@{$self->{path}})
+ {
+ $dpath = $dir->path;
+
+ # Add the (presumably local) directory to the -I flags
+ # if we're not using repositories, the directory exists,
+ # or it's Linked to a source directory (that is, it *will*
+ # exist by the time the compilation occurs).
+ $iflags .= " " . $env->{INCDIRPREFIX} . $dpath . $env->{INCDIRSUFFIX}
+ if !@param::rpath || -d $dpath || $dir->is_linked;
+ next if File::Spec->file_name_is_absolute($dpath);
+ if (@param::rpath)
+ {
+ my $d;
+ if ($dpath eq $dir::CURDIR)
+ {
+ foreach $d (map($_->path, @param::rpath))
+ {
+ $iflags .=
+ " " . $env->{INCDIRPREFIX} . $d . $env->{INCDIRSUFFIX};
+ }
+ }
+ else
+ {
+ my ($rpath);
+ foreach $d (map($_->path, @param::rpath))
+ {
+ $rpath = File::Spec->catfile($d, $dpath);
+ $iflags .=
+ " " . $env->{INCDIRPREFIX} . $rpath . $env->{INCDIRSUFFIX}
+ if -d $rpath;
+ }
+ }
+ }
+ }
+ $iflags;
+}
+
+package File::Spec;
+
+use vars qw( $_SEP $_MATCH_SEP $_MATCH_VOL );
+
+# Cons is migrating to using File::Spec for portable path name
+# manipulation. This is the right long-term direction, but there are
+# some problems with making the transition:
+#
+# For multi-volume support, we need to use newer interfaces
+# (splitpath, catpath, splitdir) that are only available in
+# File::Spec 0.8.
+#
+# File::Spec 0.8 doesn't work with Perl 5.00[34] due to
+# regular expression incompatibilities (use of \z).
+#
+# Forcing people to use a new version of a module is painful
+# because (in the workplace) their administrators aren't
+# always going to agree to install it everywhere.
+#
+# As a middle ground, we provide our own versions of all the File::Spec
+# methods we use, supporting both UNIX and Win32. Some of these methods
+# are home brew, some are cut-and-pasted from the real File::Spec methods.
+# This way, we're not reinventing the whole wheel, at least.
+#
+# We can (and should) get rid of this class whenever 5.00[34] and
+# versions of File::Spec prior to 0.9 (?) have faded sufficiently.
+# We also may need to revisit whenever someone first wants to use
+# Cons on some platform other than UNIX or Win32.
+
+BEGIN
+{
+ if ($main::_WIN32)
+ {
+ $_SEP = '\\';
+ $_MATCH_SEP = "[\Q/$_SEP\E]";
+ $_MATCH_VOL = "([a-z]:)?$_MATCH_SEP";
+ }
+ else
+ {
+ $_SEP = '/';
+ $_MATCH_SEP = "\Q$_SEP\E";
+ $_MATCH_VOL = $_MATCH_SEP;
+ }
+}
+
+sub canonpath
+{
+ my ($self, $path) = @_;
+ if ($main::_WIN32)
+ {
+ $path =~ s/^([a-z]:)/\u$1/s;
+ $path =~ s|/|\\|g;
+ $path =~ s|([^\\])\\+|$1\\|g; # xx////xx -> xx/xx
+ $path =~ s|(\\\.)+\\|\\|g; # xx/././xx -> xx/xx
+ $path =~ s|^(\.\\)+||s unless $path eq ".\\"; # ./xx -> xx
+ $path =~ s|\\$|| unless $path =~ m#^([A-Z]:)?\\$#s; # xx/ -> xx
+ }
+ else
+ {
+ $path =~ s|/+|/|g unless ($^O eq 'cygwin'); # xx////xx -> xx/xx
+ $path =~ s|(/\.)+/|/|g; # xx/././xx -> xx/xx
+ $path =~ s|^(\./)+||s unless $path eq "./"; # ./xx -> xx
+ $path =~ s|^/(\.\./)+|/|s; # /../../xx -> xx
+ $path =~ s|/$|| unless $path eq "/"; # xx/ -> xx
+ }
+ return $path;
+}
+
+sub catdir
+{
+ my $self = shift;
+ my @args = @_;
+ foreach (@args)
+ {
+
+ # append a slash to each argument unless it has one there
+ $_ .= $_SEP if $_ eq '' || substr($_, -1) ne $_SEP;
+ }
+ return $self->canonpath(join ('', @args));
+}
+
+sub catfile
+{
+ my $self = shift;
+ my $file = pop @_;
+ return $file unless @_;
+ my $dir = $self->catdir(@_);
+ $dir .= $_SEP unless substr($dir, -1) eq $_SEP;
+ $file = '' if !defined($file);
+ return $dir . $file;
+}
+
+sub catpath
+{
+ my $path = $_[1] . $_[0]->catfile(@_[2 .. $#_]);
+ $path =~ s/(.)$_MATCH_SEP*$/$1/;
+ $path;
+}
+
+sub curdir
+{
+ '.';
+}
+
+sub file_name_is_absolute
+{
+ my ($self, $file) = @_;
+ return scalar($file =~ m{^$_MATCH_VOL}is);
+}
+
+sub splitdir
+{
+ my @dirs = split (/$_MATCH_SEP/, $_[1], -1);
+ push (@dirs, '') if $dirs[$#dirs];
+ @dirs;
+}
+
+sub splitpath
+{
+ my ($self, $path) = @_;
+ my $vol = '';
+ my $sep = $_SEP;
+ if ($main::_WIN32)
+ {
+ if ($path =~ s#^([A-Za-z]:|(?:\\\\|//)[^\\/]+[\\/][^\\/]+)([\\/])#$2#)
+ {
+ $vol = $1;
+ $sep = $2;
+ }
+ }
+ my (@path) = split (/$_MATCH_SEP/, $path, -1);
+ my $file = pop @path;
+ my $dirs = join ($sep, @path, '');
+ return ($vol, $dirs, $file);
+}
+
+sub updir
+{
+ '..';
+}
+
+sub case_tolerant
+{
+ return $main::_WIN32;
+}
+
+# Directory and file handling. Files/dirs are represented by objects.
+# Other packages are welcome to add component-specific attributes.
+package dir;
+
+use vars qw( $SEPARATOR $MATCH_SEPARATOR $CURDIR $UPDIR
+ $cwd_vol %root $top $cwd );
+
+BEGIN
+{
+
+ # A portable way of determing our directory separator.
+ $SEPARATOR = File::Spec->catdir('', '');
+
+ # A fast-path regular expression to match a directory separator
+ # anywhere in a path name.
+ if ($SEPARATOR eq '/')
+ {
+ $MATCH_SEPARATOR = "\Q$SEPARATOR\E";
+ }
+ else
+ {
+ $MATCH_SEPARATOR = "[\Q/$SEPARATOR\E]";
+ }
+
+ # Cache these values so we don't have to make a method call
+ # every time we need them.
+ $CURDIR = File::Spec->curdir; # '.' on UNIX
+ $UPDIR = File::Spec->updir; # '..' on UNIX
+ #
+ $cwd_vol = '';
+}
+
+# Annotate a node (file or directory) with info about the
+# method that created it.
+sub creator
+{
+ my ($self, @frame) = @_;
+ $self->{'creator'} = \@frame if @frame;
+ $self->{'creator'};
+}
+
+# Handle a file|dir type exception. We only die if we find we were
+# invoked by something in a Conscript/Construct file, because
+# dependencies created directly by Cons' analysis shouldn't cause
+# an error.
+sub _type_exception
+{
+ my ($e) = @_;
+ my ($line, $sub);
+ (undef, undef, $line, $sub) = script::caller_info;
+ if (defined $line)
+ {
+ my $err =
+ "\"${\$e->path}\" already in use as a "
+ . ref($e)
+ . " before $sub on line $line";
+ if ($e->{'creator'})
+ {
+ my $script;
+ (undef, $script, $line, $sub) = @{$e->{'creator'}};
+ $err =
+ "\t" . $err . ",\n\t\tdefined by $sub in $script, line $line";
+ }
+ $err .= "\n";
+ die $err;
+ }
+}
+
+# This wraps up all the common File::Spec logic that we use for parsing
+# directory separators in a path and turning it into individual
+# subdirectories that we must create, as well as creation of root
+# nodes for any new file system volumes we find. File::Spec doesn't have
+# intuitively obvious interfaces, so this is heavily commented.
+#
+# Note: This is NOT an object or class method;
+# it's just a utility subroutine.
+sub _parse_path
+{
+ my ($dir, $path) = @_;
+
+ # Convert all slashes to the native directory separator.
+ # This allows Construct files to always be written with good
+ # old POSIX path names, regardless of what we're running on.
+ $path = File::Spec->canonpath($path);
+
+ # File::Spec doesn't understand the Cons convention of
+ # an initial '#' for top-relative files. Strip it.
+ my ($toprel) = $path =~ s/^#//;
+
+ # Let File::Spec do the heavy lifting of parsing the path name.
+ my ($vol, $directories, $entry) = File::Spec->splitpath($path);
+ my @dirs = File::Spec->splitdir($directories);
+
+ # If there was a file entry on the end of the path, then the
+ # last @dirs element is '' and we don't need it. If there
+ # wasn't a file entry on the end (File::Spec->splitpath() knew
+ # the last component was a directory), then the last @dirs
+ # element becomes the entry we want to look up.
+ my ($e) = pop @dirs;
+ $entry = $e if $entry eq '';
+
+ if (File::Spec->file_name_is_absolute($path))
+ {
+
+ # An absolute path name. If no volume was supplied,
+ # use the volume of our current directory.
+ $vol = $cwd_vol if $vol eq '';
+ $vol = uc($vol) if File::Spec->case_tolerant;
+ if (!defined $root{$vol})
+ {
+
+ # This is our first time looking up a path name
+ # on this volume, so create a root node for it.
+ # (On UNIX systems, $vol is always '', so '/'
+ # always maps to the $root{''} node.)
+ $root{$vol} = {
+ path => $vol . $SEPARATOR,
+ prefix => $vol . $SEPARATOR,
+ srcpath => $vol . $SEPARATOR,
+ 'exists' => 1
+ };
+ $root{$vol}->{'srcdir'} = $root{$vol};
+ bless $root{$vol};
+ }
+
+ # We're at the top, so strip the blank entry from the front of
+ # the @dirs array since the initial '/' it represents will now
+ # be supplied by the root node we return.
+ shift @dirs;
+ $dir = $root{$vol};
+ }
+ elsif ($toprel)
+ {
+ $dir = $dir::top;
+ }
+ ($dir, \@dirs, $entry);
+}
+
+# Common subroutine for creating directory nodes.
+sub _create_dirs
+{
+ my ($dir, @dirs) = @_;
+ my $e;
+ foreach $e (@dirs)
+ {
+ my $d = $dir->{member}->{$e};
+ if (!defined $d)
+ {
+ bless $d = {'entry' => $e, 'dir' => $dir,}, 'dir';
+ $d->creator(script::caller_info);
+ $d->{member}->{$dir::CURDIR} = $d;
+ $d->{member}->{$dir::UPDIR} = $dir;
+ $dir->{member}->{$e} = $d;
+ }
+ elsif (ref $d eq 'entry')
+ {
+ bless $d, 'dir';
+ $d->{member}->{$dir::CURDIR} = $d;
+ $d->{member}->{$dir::UPDIR} = $dir;
+ }
+ elsif (ref $d eq 'file')
+ {
+
+ # This clause is to supply backwards compatibility,
+ # with a warning, for anyone that's used FilePath
+ # to refer to a directory. After people have using
+ # 1.8 have had time to adjust (sometime in version
+ # 1.9 or later), we should remove this entire clause.
+ my ($script, $line, $sub);
+ (undef, $script, $line, $sub) = @{$d->{'creator'}};
+ if ($sub eq 'script::FilePath')
+ {
+ print STDERR
+ "$0: Warning: $sub used to refer to a directory\n"
+ . "\tat line $line of $script. Use DirPath instead.\n";
+ bless $d, 'dir';
+ }
+ else
+ {
+ _type_exception($d);
+ }
+ }
+ elsif (ref $d ne 'dir')
+ {
+ _type_exception($d);
+ }
+ $dir = $d;
+ }
+ $dir;
+}
+
+# Look up an entry in a directory. This method is for when we don't
+# care whether a file or directory is returned, so if the entry already
+# exists, it will simply be returned. If not, we create it as a
+# generic "entry" which can be later turned into a file or directory
+# by a more-specific lookup.
+#
+# The file entry may be specified as relative, absolute (starts with /),
+# or top-relative (starts with #).
+sub lookup
+{
+ my ($dir, $entry) = @_;
+
+ if ($entry !~ m#$MATCH_SEPARATOR#o)
+ {
+
+ # Fast path: simple entry name in a known directory.
+ if ($entry =~ s/^#//)
+ {
+
+ # Top-relative names begin with #.
+ $dir = $dir::top;
+ }
+ elsif ($entry =~ s/^!//)
+ {
+ $dir = $dir::cwd->srcdir;
+ }
+ }
+ else
+ {
+ my $dirsref;
+ ($dir, $dirsref, $entry) = _parse_path($dir, $entry);
+ $dir = _create_dirs($dir, @$dirsref) if @$dirsref;
+ return if !defined $dir;
+ return $dir if $entry eq '';
+ }
+
+ my $e = $dir->{member}->{$entry};
+ if (!defined $e)
+ {
+ bless $e = {'entry' => $entry, 'dir' => $dir,}, 'entry';
+ $e->creator(script::caller_info);
+ $dir->{member}->{$entry} = $e;
+ }
+
+ $e;
+}
+
+# Look up a file entry in a directory.
+#
+# The file entry may be specified as relative, absolute (starts with /),
+# or top-relative (starts with #).
+sub lookupfile
+{
+ my ($dir, $entry) = @_;
+
+ if ($entry !~ m#$MATCH_SEPARATOR#o)
+ {
+
+ # Fast path: simple entry name in a known directory.
+ if ($entry =~ s/^#//)
+ {
+
+ # Top-relative names begin with #.
+ $dir = $dir::top;
+ }
+ elsif ($entry =~ s/^!//)
+ {
+ $dir = $dir::cwd->srcdir;
+ }
+ }
+ else
+ {
+ my $dirsref;
+ ($dir, $dirsref, $entry) = _parse_path($dir, $entry);
+ $dir = _create_dirs($dir, @$dirsref) if @$dirsref;
+ return undef if $entry eq '';
+ }
+
+ my $f = $dir->{member}->{$entry};
+ if (!defined $f)
+ {
+ bless $f = {'entry' => $entry, 'dir' => $dir,}, 'file';
+ $f->creator(script::caller_info);
+ $dir->{member}->{$entry} = $f;
+ }
+ elsif (ref $f eq 'entry')
+ {
+ bless $f, 'file';
+ }
+ elsif (ref $f ne 'file')
+ {
+ _type_exception($f);
+ }
+
+ $f;
+}
+
+# Look up a (sub-)directory entry in a directory.
+#
+# The (sub-)directory entry may be specified as relative, absolute
+# (starts with /), or top-relative (starts with #).
+sub lookupdir
+{
+ my ($dir, $entry) = @_;
+
+ my $dirsref;
+ if ($entry !~ m#$MATCH_SEPARATOR#o)
+ {
+
+ # Fast path: simple entry name in a known directory.
+ if ($entry =~ s/^#//)
+ {
+
+ # Top-relative names begin with #.
+ $dir = $dir::top;
+ }
+ elsif ($entry =~ s/^!//)
+ {
+ $dir = $dir::cwd->srcdir;
+ }
+ }
+ else
+ {
+ ($dir, $dirsref, $entry) = _parse_path($dir, $entry);
+ }
+ push (@$dirsref, $entry) if $entry ne '';
+ _create_dirs($dir, @$dirsref);
+}
+
+# Look up a file entry and return it if it's accessible.
+sub lookup_accessible
+{
+ my $file = $_[0]->lookupfile($_[1]);
+ return ($file && $file->accessible) ? $file : undef;
+}
+
+# Return the parent directory without doing a lookupdir,
+# which would create a parent if it doesn't already exist.
+# A return value of undef (! $dir->up) indicates a root directory.
+sub up
+{
+ $_[0]->{member}->{$dir::UPDIR};
+}
+
+# Return whether this is an entry somewhere underneath the
+# specified directory.
+sub is_under
+{
+ my $dir = $_[0];
+ while ($dir)
+ {
+ return 1 if $_[1] == $dir;
+ $dir = $dir->up;
+ }
+ return undef;
+}
+
+# Return the relative path from the calling directory ($_[1])
+# to the object. If the object is not under the directory, then
+# we return it as a top-relative or absolute path name.
+sub relpath
+{
+ my ($dir, $obj) = @_;
+ my @dirs;
+ my $o = $obj;
+ while ($o)
+ {
+ if ($dir == $o)
+ {
+ if (@dirs < 2)
+ {
+ return $dirs[0] || '';
+ }
+ else
+ {
+ return File::Spec->catdir(@dirs);
+ }
+ }
+ unshift (@dirs, $o->{entry});
+ $o = $o->up;
+ }
+
+ # The object was not underneath the specified directory.
+ # Use the node's cached path, which is either top-relative
+ # (in which case we append '#' to the beginning) or
+ # absolute.
+ my $p = $obj->path;
+ $p = '#' . $p if !File::Spec->file_name_is_absolute($p);
+ return $p;
+}
+
+# Return the path of the directory (file paths implemented
+# separately, below).
+sub path
+{
+ $_[0]->{path} || ($_[0]->{path} = $_[0]->{dir}->prefix . $_[0]->{entry});
+}
+
+# Return the pathname as a prefix to be concatenated with an entry.
+sub prefix
+{
+ return $_[0]->{prefix} if exists $_[0]->{prefix};
+ $_[0]->{prefix} = $_[0]->path . $SEPARATOR;
+}
+
+# Return the related source path prefix.
+sub srcprefix
+{
+ return $_[0]->{srcprefix} if exists $_[0]->{srcprefix};
+ my ($srcdir) = $_[0]->srcdir;
+ $srcdir->{srcprefix} =
+ $srcdir eq $_[0] ? $srcdir->prefix : $srcdir->srcprefix;
+}
+
+# Return the related source directory.
+sub srcdir
+{
+ $_[0]->{'srcdir'}
+ || ($_[0]->{'srcdir'} = $_[0]->{dir}->srcdir->lookupdir($_[0]->{entry}));
+}
+
+# Return if the directory is linked to a separate source directory.
+sub is_linked
+{
+ return $_[0]->{is_linked} if defined $_[0]->{is_linked};
+ $_[0]->{is_linked} = $_[0]->path ne $_[0]->srcdir->path;
+}
+
+sub link
+{
+ my (@paths) = @_;
+ my ($srcdir) = $dir::cwd->lookupdir(pop @paths)->srcdir;
+ map($dir::cwd->lookupdir($_)->{'srcdir'} = $srcdir, @paths);
+
+ # make a reverse lookup for the link.
+ $srcdir->{links} = [] if !$srcdir->{links};
+ push @{$srcdir->{links}}, @paths;
+}
+
+use vars qw( @tail ); # TODO: Why global ????
+
+sub linked_targets
+{
+ my $tgt = shift;
+ my @targets = ();
+ my $dir;
+ if (ref $tgt eq 'dir')
+ {
+ $dir = $tgt;
+ }
+ else
+ {
+ push @tail, $tgt;
+ $dir = $tgt->{dir};
+ }
+ while ($dir)
+ {
+ if (defined $dir->{links} && @{$dir->{links}})
+ {
+ push @targets, map(File::Spec->catdir($_, @tail), @{$dir->{links}});
+
+ #print STDERR "Found Link: ${\$dir->path} -> @{\$dir->{links}}\n";
+ }
+ unshift @tail, $dir->{entry};
+ $dir = $dir->up;
+ }
+
+ return map($dir::top->lookupdir($_), @targets);
+}
+
+sub accessible
+{
+ my $path = $_[0]->path;
+ my $err =
+ "$0: you have attempted to use path \"$path\" both as a file "
+ . "and as a directory!\n";
+ die $err;
+}
+
+sub init
+{
+ my $path = Cwd::cwd();
+
+ # We know we can get away with passing undef to lookupdir
+ # as the directory because $dir is an absolute path.
+ $top = lookupdir(undef, $path);
+ $top->{'path'} = $top->{srcpath} = $dir::CURDIR;
+ $top->{'prefix'} = '';
+ $top->{'srcdir'} = $top;
+
+ $cwd = $top;
+
+ ($cwd_vol, undef, undef) = File::Spec->splitpath($path);
+ $cwd_vol = '' if !defined $cwd_vol;
+ $cwd_vol = uc($cwd_vol) if File::Spec->case_tolerant;
+}
+
+package file;
+
+use vars qw( @ISA $level );
+
+BEGIN { @ISA = qw(dir); $level = 0 }
+
+# Return the pathname of the file.
+# Define this separately from dir::path because we don't want to
+# cache all file pathnames (just directory pathnames).
+sub path
+{
+ $_[0]->{dir}->prefix . $_[0]->{entry};
+}
+
+# Return the related source file path.
+sub srcpath
+{
+ $_[0]->{dir}->srcprefix . $_[0]->{entry};
+}
+
+# Return if the file is (should be) linked to a separate source file.
+sub is_linked
+{
+ $_[0]->{dir}->is_linked;
+}
+
+# Repository file search. If the local file exists, that wins.
+# Otherwise, return the first existing same-named file under a
+# Repository directory. If there isn't anything with the same name
+# under a Repository directory, return the local file name anyway
+# so that some higher layer can try to construct it.
+sub rfile
+{
+ return $_[0]->{rfile} if exists $_[0]->{rfile};
+ my ($self) = @_;
+ my ($rfile) = $self;
+ if (@param::rpath)
+ {
+ my ($path) = $self->path;
+ if (!File::Spec->file_name_is_absolute($path) && !-f $path)
+ {
+ my ($dir);
+ foreach $dir (@param::rpath)
+ {
+ my ($t) = $dir->prefix . $path;
+ if (-f $t)
+ {
+ $rfile = $_[0]->lookupfile($t);
+ $rfile->{'lfile'} = $self;
+ last;
+ }
+ }
+ }
+ }
+ $self->{rfile} = $rfile;
+}
+
+# Returns the local file for a repository file;
+# returns self if it's already a local file.
+sub lfile
+{
+ $_[0]->{'lfile'} || $_[0];
+}
+
+# returns the "precious" status of this file.
+sub precious
+{
+ return $_[0]->{precious};
+}
+
+# "Erase" reference to a Repository file,
+# making this a completely local file object
+# by pointing it back to itself.
+sub no_rfile
+{
+ $_[0]->{'rfile'} = $_[0];
+}
+
+# Return a path to the first existing file under a Repository directory,
+# implicitly returning the current file's path if there isn't a
+# same-named file under a Repository directory.
+sub rpath
+{
+ $_[0]->{rpath} || ($_[0]->{rpath} = $_[0]->rfile->path);
+}
+
+# Return a path to the first linked srcpath file under a Repositoy
+# directory, implicitly returning the current file's srcpath if there
+# isn't a same-named file under a Repository directory.
+sub rsrcpath
+{
+ return $_[0]->{rsrcpath} if exists $_[0]->{rsrcpath};
+ my ($self) = @_;
+ my ($path) = $self->{rsrcpath} = $self->srcpath;
+ if (@param::rpath && !File::Spec->file_name_is_absolute($path) && !-f $path)
+ {
+ my ($dir);
+ foreach $dir (@param::rpath)
+ {
+ my ($t) = $dir->prefix . $path;
+ if (-f $t)
+ {
+ $self->{rsrcpath} = $t;
+ last;
+ }
+ }
+ }
+ $self->{rsrcpath};
+}
+
+# Return if a same-named file source file exists.
+# This handles the interaction of Link and Repository logic.
+# As a side effect, it will link a source file from its Linked
+# directory (preferably local, but maybe in a repository)
+# into a build directory from its proper Linked directory.
+sub source_exists
+{
+ return $_[0]->{source_exists} if defined $_[0]->{source_exists};
+ my ($self) = @_;
+ my ($path) = $self->path;
+ my ($mtime, $ctime) = (stat($path))[9, 10];
+ if ($self->is_linked)
+ {
+
+ # Linked directory, local logic.
+ my ($srcpath) = $self->srcpath;
+ my ($src_mtime, $src_ctime) = (stat($srcpath))[9, 10];
+ if ($src_mtime)
+ {
+ if (!$mtime || $src_mtime != $mtime || $src_ctime != $ctime)
+ {
+ futil::install($srcpath, $self);
+ }
+ return $self->{source_exists} = 1;
+ }
+
+ # Linked directory, repository logic.
+ if (@param::rpath)
+ {
+ if ($self != $self->rfile)
+ {
+ return $self->{source_exists} = 1;
+ }
+ my ($rsrcpath) = $self->rsrcpath;
+ if ($path ne $rsrcpath)
+ {
+ my ($rsrc_mtime, $rsrc_ctime) = (stat($rsrcpath))[9, 10];
+ if ($rsrc_mtime)
+ {
+ if (!$mtime
+ || $rsrc_mtime != $mtime
+ || $rsrc_ctime != $ctime)
+ {
+ futil::install($rsrcpath, $self);
+ }
+ return $self->{source_exists} = 1;
+ }
+ }
+ }
+
+ # There was no source file in any Linked directory
+ # under any Repository. If there's one in the local
+ # build directory, it no longer belongs there.
+ if ($mtime)
+ {
+ unlink($path) || die ("$0: couldn't unlink $path ($!)\n");
+ }
+ return $self->{source_exists} = '';
+ }
+ else
+ {
+ if ($mtime)
+ {
+ return $self->{source_exists} = 1;
+ }
+ if (@param::rpath && $self != $self->rfile)
+ {
+ return $self->{source_exists} = 1;
+ }
+ return $self->{source_exists} = '';
+ }
+}
+
+# Return if a same-named derived file exists under a Repository directory.
+sub derived_exists
+{
+ $_[0]->{derived_exists}
+ || ($_[0]->{derived_exists} = ($_[0] != $_[0]->rfile));
+}
+
+# Return if this file is somewhere under a Repository directory.
+sub is_on_rpath
+{
+ defined $_[0]->{'lfile'};
+}
+
+sub local
+{
+ my ($self, $arg) = @_;
+ if (defined $arg)
+ {
+ $self->{'local'} = $arg;
+ }
+ $self->{'local'};
+}
+
+# Return the entry name of the specified file with the specified
+# suffix appended. Leave it untouched if the suffix is already there.
+# Differs from the addsuffix function, below, in that this strips
+# the existing suffix (if any) before appending the desired one.
+sub base_suf
+{
+ my ($entry) = $_[0]->{entry};
+ if ($entry !~ m/$_[1]$/)
+ {
+ $entry =~ s/\.[^\.]*$//;
+ $entry .= $_[1];
+ }
+ $entry;
+}
+
+# Return the suffix of the file; everything including and to the
+# right of the last dot.
+sub suffix
+{
+ my @pieces = split (/\./, $_[0]->{entry});
+ my $suffix = pop (@pieces);
+ return ".$suffix";
+}
+
+# Called as a simple function file::addsuffix(name, suffix)
+sub addsuffix
+{
+ my ($name, $suffix) = @_;
+
+ if ($suffix && substr($name, -length($suffix)) ne $suffix)
+ {
+ return $name .= $suffix;
+ }
+ $name;
+}
+
+# Return true if the file is (or will be) accessible.
+# That is, if we can build it, or if it is already present.
+sub accessible
+{
+ (exists $_[0]->{builder}) || ($_[0]->source_exists);
+}
+
+# Return true if the file should be ignored for the purpose
+# of computing dependency information (should not be considered
+# as a dependency and, further, should not be scanned for
+# dependencies).
+sub ignore
+{
+ return 0 if !$param::ignore;
+ return $_[0]->{ignore} if exists $_[0]->{ignore};
+ $_[0]->{ignore} = $_[0]->path =~ /$param::ignore/o;
+}
+
+# Build the file, if necessary.
+sub build
+{
+ return $_[0]->{status} if $_[0]->{status};
+ my ($status) = &file::_build;
+ if ($_[0]->{after_build_func})
+ {
+
+ #print STDERR "DEBUG: after_build_func=$_[0]->{after_build_func}\n";
+ my ($pkgvars) = $_[0]->{conscript}->{pkgvars};
+ NameSpace::restore('script', $pkgvars) if $pkgvars;
+ eval("package script; " . $_[0]->{after_build_func});
+ print "Error running AfterBuild for ${\$_[0]->path}: $@\n" if ($@);
+ NameSpace::remove('script', keys %$pkgvars) if $pkgvars;
+ }
+ return $status;
+}
+
+sub _build
+{
+ my @args = @_;
+
+ if ($param::max_jobs <= 1)
+ { # pcons
+ my ($self) = @args;
+ print main::DEPFILE $self->path, "\n" if $param::depfile;
+ print((' ' x $level), "Checking ", $self->path, "\n")
+ if $param::depends;
+ if (!exists $self->{builder})
+ {
+
+ # We don't know how to build the file. This is OK, if
+ # the file is present as a source file, under either the
+ # local tree or a Repository.
+ if ($self->source_exists)
+ {
+ return $self->{status} = 'handled';
+ }
+ else
+ {
+ my ($name) = $self->path;
+ print("$0: don't know how to construct \"$name\"\n");
+ exit(1) unless $param::kflag;
+ return $self->{status} = 'errors'; # xxx used to be 'unknown'
+ }
+ }
+
+ # An associated build object exists, so we know how to build
+ # the file. We first compute the signature of the file, based
+ # on its dependendencies, then only rebuild the file if the
+ # signature has changed.
+ my ($builder) = $self->{builder};
+ $level += 2;
+
+ my (@deps) = (@{$self->{dep}}, @{$self->{sources}});
+ my ($rdeps) = \@deps;
+
+ if ($param::random)
+ {
+
+ # If requested, build in a random order, instead of the
+ # order that the dependencies were listed.
+ my (%rdeps);
+ map { $rdeps{$_, '*' x int(rand 10)} = $_ } @deps;
+ $rdeps = [values(%rdeps)];
+ }
+
+ $self->{status} = '';
+
+ my $dep;
+ for $dep (@$rdeps)
+ {
+ if ((build $dep) eq 'errors')
+ {
+
+ # Propagate dependent errors to target.
+ # but try to build all dependents regardless of errors.
+ $self->{status} = 'errors';
+ }
+ }
+
+ # If any dependents had errors, then we abort.
+ if ($self->{status} eq 'errors')
+ {
+ $level -= 2;
+ return 'errors';
+ }
+
+ # Compute the final signature of the file, based on
+ # the static dependencies (in order), dynamic dependencies,
+ # output path name, and (non-substituted) build script.
+ my ($sig) =
+ 'sig'->collect(map('sig'->signature($_->rfile), @deps),
+ $builder->includes($self), $builder->scriptsig);
+
+ # May have gotten errors during computation of dynamic
+ # dependency signature, above.
+ $level -= 2;
+ return 'errors' if $self->{status} eq 'errors';
+
+ if (@param::rpath && $self->derived_exists)
+ {
+
+ # There is no local file of this name, but there is one
+ # under a Repository directory.
+
+ if ('sig'->current($self->rfile, $sig))
+ {
+
+ # The Repository copy is current (its signature matches
+ # our calculated signature).
+ if ($self->local)
+ {
+
+ # ...but they want a local copy, so provide it.
+ main::showcom("Local copy of ${\$self->path} from "
+ . "${\$self->rpath}");
+ futil::install($self->rpath, $self);
+ 'sig'->bsig($self, $sig);
+ }
+ return $self->{status} = 'handled';
+ }
+
+ # The signatures don't match, implicitly because something
+ # on which we depend exists locally. Get rid of the reference
+ # to the Repository file; we'll build this (and anything that
+ # depends on it) locally.
+ $self->no_rfile;
+ }
+
+ # Then check for currency.
+ if (!'sig'->current($self, $sig))
+ {
+
+ # We have to build/derive the file.
+ print((' ' x $level), "Rebuilding ", $self->path,
+ ": out of date.\n")
+ if $param::depends;
+
+ # First check to see if the built file is cached.
+ if ($builder->cachin($self, $sig))
+ {
+ 'sig'->bsig($self, $sig);
+ return $self->{status} = 'built';
+ }
+ elsif ($builder->action($self))
+ {
+ $builder->cachout($self, $sig);
+ 'sig'->bsig($self, $sig);
+ return $self->{status} = 'built';
+ }
+ else
+ {
+ die ("$0: errors constructing ${\$self->path}\n")
+ unless $param::kflag;
+ return $self->{status} = 'errors';
+ }
+ }
+ else
+ {
+
+ # Push this out to the cache if we've been asked to (-C option).
+ # Don't normally do this because it slows us down.
+ # In a fully built system, no accesses to the cache directory
+ # are required to check any files. This is a win if cache is
+ # heavily shared. Enabling this option puts the directory in the
+ # loop. Useful only when you wish to recreate a cache from a build.
+ if ($param::cachesync)
+ {
+ $builder->cachout($self, $sig);
+ 'sig'->bsig($self, $sig);
+ }
+ return $self->{status} = 'handled';
+ }
+ }
+ else
+ { # pcons
+ my ($tgt) = @args; # pcons
+ local ($file::child_queue) = {parent => $tgt}; # pcons
+ _pbuild($tgt); # pcons
+ wait_on_all_children(); # pcons
+ return $tgt->{status}; # pcons
+ }
+}
+
+########################################
+# pcons only BEGIN
+#
+
+sub pbuild
+{
+ $_[0]->{status} || &file::_pbuild;
+}
+
+sub _pbuild
+{
+ my ($self) = @_;
+ $self->{status} = ''; # tgl
+ print main::DEPFILE $self->path, "\n" if param::depfile;
+ print((' ' x $level), $self->path, "\n") if $param::depends;
+ if (!exists $self->{builder})
+ {
+
+ # We don't know how to build the file. This is OK, if
+ # the file is present as a source file, under either the
+ # local tree or a Repository.
+ if ($self->source_exists)
+ {
+ return $self->{status} = 'handled';
+ }
+ else
+ {
+ my ($name) = $self->path;
+ print("$0: don't know how to construct \"$name\"\n");
+ exit(1) unless $param::kflag;
+ return $self->{status} = 'errors'; # xxx used to be 'unknown'
+ }
+ }
+
+ # An associated build object exists, so we know how to build
+ # the file. We first compute the signature of the file, based
+ # on its dependendencies, then only rebuild the file if the
+ # signature has changed.
+ my ($builder) = $self->{'builder'};
+ $level += 2;
+
+ my (@deps) = (@{$self->{dep}}, @{$self->{sources}});
+ my ($rdeps) = \@deps;
+
+ if ($param::random)
+ {
+
+ # If requested, build in a random order, instead of the
+ # order that the dependencies were listed.
+ my (%rdeps);
+
+ # FIX map { $rdeps{$_,'*' x int(rand(0,10))} = $_ } @deps;
+ map { $rdeps{$_, '*' x int(rand(10))} = $_ } @deps;
+ $rdeps = [values(%rdeps)];
+ }
+
+ $self->{status} = '';
+
+ my $sig;
+ {
+
+ # print "in ", $self->path, "\n";
+ local ($file::child_queue) = {parent => $self};
+ for my $dep (@$rdeps)
+ {
+ if ((pbuild $dep) eq 'errors')
+ {
+
+ # Propagate dependent errors to target.
+ # but try to build all dependents regardless of errors.
+ $self->{status} = 'errors';
+ }
+ }
+ wait_on_all_children();
+
+ }
+
+ # If any dependents had errors, then we abort.
+ if ($self->{'status'} eq 'errors')
+ {
+ $level -= 2;
+ return 'errors';
+ }
+
+ # Compute the final signature of the file, based on
+ # the static dependencies (in order), dynamic dependencies,
+ # output path name, and (non-substituted) build script.
+
+ # my($sig) = $self->{'sign'} = sig->collect(map(sig->signature($_->rfile), @deps),
+ # $builder->includes($self),
+ ## FIX $builder->script);
+ # $builder->scriptsig);
+
+ my ($sig) = 'sig'->collect(
+ map('sig'->signature($_->rfile), @deps), # from cons-2.3.0
+ $builder->includes($self), # from cons-2.3.0
+ $builder->scriptsig
+ ); # from cons-2.3.0
+ $self->{sign} = $sig;
+
+ # May have gotten errors during computation of dynamic
+ # dependency signature, above.
+ $level -= 2;
+ return 'errors' if $self->{status} eq 'errors';
+
+ if (@param::rpath && $self->derived_exists)
+ {
+
+ # There is no local file of this name, but there is one
+ # under a Repository directory.
+
+ if ('sig'->current($self->rfile, $sig))
+ {
+
+ # The Repository copy is current (its signature matches
+ # our calculated signature).
+ if ($self->local)
+ {
+
+ # ...but they want a local copy, so provide it.
+ main::showcom(
+ "Local copy of ${\$self->path} from ${\$self->rpath}");
+ futil::install($self->rpath, $self);
+
+ #'sig'->set($self, $sig);
+ 'sig'->bsig($self, $sig); # pcons
+ }
+ return $self->{status} = 'handled';
+ }
+
+ # The signatures don't match, implicitly because something
+ # on which we depend exists locally. Get rid of the reference
+ # to the Repository file; we'll build this (and anything that
+ # depends on it) locally.
+ $self->no_rfile;
+ }
+
+ # Then check for currency.
+ if (!'sig'->current($self, $sig))
+ {
+
+ # We have to build/derive the file.
+ # First check to see if the built file is cached.
+ if ($builder->cachin($self, $sig))
+ {
+
+ #'sig'->set($self, $sig);
+ 'sig'->bsig($self, $sig); # pcons
+ return $self->{status} = 'built';
+
+ # action no longer blocks (for most actions), so this returns
+ # immediately, before any commands are actually run. The
+ # signature and return status should be overridden later in
+ # wait_on_child if a process was forked, but they are still set
+ # here in case a non-spawning action was called
+ }
+ elsif ($builder->action($self))
+ {
+ $builder->cachout($self, $sig);
+
+ #'sig'->set($self, $sig);
+ 'sig'->bsig($self, $sig); # pcons
+ return $self->{status} = 'built';
+ }
+ else
+ {
+ die ("$0: errors constructing ${\$self->path}\n")
+ unless $param::kflag;
+ return $self->{status} = 'errors';
+ }
+ }
+ else
+ {
+
+ # Push this out to the cache if we've been asked to (-C option).
+ # Don't normally do this because it slows us down.
+ # In a fully built system, no accesses to the cache directory
+ # are required to check any files. This is a win if cache is
+ # heavily shared. Enabling this option puts the directory in the
+ # loop. Useful only when you wish to recreate a cache from a build.
+ if ($param::cachesync)
+ {
+ $builder->cachout($self, $sig);
+
+ #'sig'->set($self, $sig);
+ 'sig'->bsig($self, $sig);
+ }
+ return $self->{status} = 'handled';
+ }
+}
+
+my @finished; # pcons //fix -Mstrict
+
+sub wait_on_max_jobs
+{
+ while (grep(/\d+/, keys %{$file::child_queue}) >= $param::max_jobs)
+ {
+ wait_on_child();
+ }
+}
+
+sub wait_on_all_children
+{
+
+ while (grep(/\d+/, keys %{$file::child_queue}) != 0)
+ {
+ wait_on_child();
+ }
+}
+
+sub wait_on_child
+{
+ my ($queue) = $file::child_queue;
+ my ($parent) = $queue->{parent};
+
+ my $n = grep(/\d+/, keys %{$file::child_queue});
+
+ # printf "Waiting to build (%s): %s\n", $n, $parent->path if ref($parent) eq 'file';
+ my $pid = undef;
+
+ # first check for jobs that have already been collected from child
+ # targets
+ for my $p (@finished)
+ {
+ if ($queue->{$p})
+ {
+ $pid = $p;
+ last;
+ }
+ }
+
+ while (!$queue->{$pid})
+ {
+ $pid = wait();
+ if ($pid < 1)
+ {
+ die "wait returned invalid pid $pid";
+ }
+ else
+ {
+
+ # this job was meant for a parent, save it so that it can be
+ # found later
+ if (!$queue->{$pid})
+ {
+ push @finished, $pid;
+ }
+ }
+ }
+
+ my $child = $queue->{$pid};
+ my $tgt = $queue->{$pid}->{'tgt'};
+ my $com = $child->{'com'};
+ $tgt->{status} = 'built';
+
+ #print "waited: ", $tgt->path;
+ #print "on: ", keys %{$queue}, "\n";
+
+ my ($b0, $b1) = ($? & 0xFF, $? >> 8);
+
+ # Don't actually see 0177 on stopped process; is this necessary?
+ # next if $b0 == 0177; # process stopped; we can wait.
+ if ($b0)
+ {
+ my ($core, $sig) = ($b0 & 0200, $b0 & 0177);
+ my ($coremsg) = $core ? "; core dumped" : "";
+ $com =~ s/\s.*//;
+ my ($path) = $tgt->path;
+ warn qq($0: *** [$path] $com terminated by signal $sig$coremsg\n);
+ $parent->{status} = $tgt->{status} = 'errors';
+ }
+ if ($b1)
+ {
+ my ($path) = $tgt->path;
+ warn qq($0: *** [$path] Error $b1\n); # trying to be like make.
+ $parent->{status} = $tgt->{status} = 'errors';
+ }
+ if ($tgt->{status} eq 'built')
+ {
+ $tgt->{builder}->cachout($tgt, $tgt->{sign});
+
+ #'sig'->set($tgt, $tgt->{sign});
+ 'sig'->bsig($tgt, $tgt->{sign});
+ }
+ else
+ {
+ die ("$0: errors constructing ${\$tgt->path}\n") unless $param::kflag;
+ }
+ delete $queue->{$pid};
+
+}
+
+#
+# pcons END
+########################################
+
+# Bind an action to a file, with the specified sources. No return value.
+sub bind
+{
+ my ($self, $builder, @sources) = @_;
+ if ($self->{builder} && !$self->{builder}->compatible($builder))
+ {
+
+ # Even if not "compatible", we can still check to see if the
+ # derivation is identical. It should be identical if the builder is
+ # the same and the sources are the same.
+ if ("$self->{builder} @{$self->{sources}}" ne "$builder @sources")
+ {
+ $main::errors++;
+ my ($_foo1, $script1, $line1, $sub1) = @{$self->creator};
+ my ($_foo2, $script2, $line2, $sub2) = script::caller_info;
+ my $err =
+ "\t${\$self->path}\n"
+ . "\tbuilt (at least) two different ways:\n"
+ . "\t\t$script1, line $line1: $sub1\n"
+ . "\t\t$script2, line $line2: $sub2\n";
+ die $err;
+ }
+ return;
+ }
+ if ($param::wflag)
+ {
+ my ($script, $line, $sub);
+ (undef, $script, $line, $sub) = script::caller_info;
+ $self->{script} = '' if !defined $self->{script};
+ $self->{script} .= "; " if $self->{script};
+ $self->{script} .= qq($sub in "$script", line $line);
+ }
+ $self->{builder} = $builder;
+ push (@{$self->{sources}}, @sources);
+ @{$self->{dep}} = () if !defined $self->{dep};
+ $self->{conscript} = $priv::self->{script};
+}
+
+sub is_under
+{
+ $_[0]->{dir}->is_under($_[1]);
+}
+
+sub relpath
+{
+ my $dirpath = $_[0]->relpath($_[1]->{dir});
+ if (!$dirpath)
+ {
+ return $_[1]->{entry};
+ }
+ else
+ {
+ File::Spec->catfile($dirpath, $_[1]->{entry});
+ }
+}
+
+# Return the signature array for this file.
+# This probably belongs in its own "sigarray" package,
+# which would make it easier to optimize performance.
+sub sigarray
+{
+ if ($_[0]->{sigaref})
+ {
+ return @{$_[0]->{sigaref}};
+ }
+ my $self = shift;
+
+ # glob2pat based on The Perl Cookbook, p. 180.
+ sub glob2pat
+ {
+ my $globstr = shift;
+ my %patmap = (
+ '*' => '.*',
+ '?' => '.',
+ '[' => '[',
+ ']' => ']',
+ '/' => "\Q$dir::SEPARATOR", # Cons-specific modification
+ );
+ $globstr =~ s{(.)} { $patmap{$1} || "\Q$1" }ge;
+ return '^' . $globstr . '$';
+ }
+ my @sigarray;
+ my $default;
+ my $builder = $self->lfile->{builder};
+ if (!$builder)
+ {
+ @sigarray = @$param::sourcesig;
+ $default = [qw(content)];
+ }
+ else
+ {
+ if ($builder->{env} && $builder->{env}->{SIGNATURE})
+ {
+ @sigarray = @{$builder->{env}->{SIGNATURE}};
+ }
+ else
+ {
+ my $class = ref $builder;
+ my $path = $self->path;
+ warn qq($0: Warning: Builder package $class did not record\n)
+ . qq(\tthe calling environment for '$path'.\n)
+ . qq(\tUnable to use any %SIGNATURE construction variable\n)
+ . qq(\tfor signature configuration.\n);
+ }
+ $default = [qw(build)];
+ }
+ my $path = $self->path;
+ while (@sigarray)
+ {
+ my ($glob, $aref) = splice(@sigarray, 0, 2);
+ my $re = glob2pat($glob);
+ if ($path =~ /$re/)
+ {
+ $aref = [split (/\s+/, $aref)] if !ref $aref;
+ $self->{sigaref} = $aref;
+ return @$aref;
+ }
+ }
+ $self->{sigaref} = $default;
+ return @{$self->{sigaref}};
+}
+
+# Decide if this file's signature should be the content or build signature.
+sub sigtype
+{
+ if ($_[0]->{sigtype})
+ {
+ return $_[0]->{sigtype};
+ }
+ my $self = shift;
+ my @sigarray = $self->sigarray;
+ my $sigtype;
+ if (grep($_ eq "build", @sigarray))
+ {
+ $sigtype = 'bsig';
+ }
+ elsif (grep($_ =~ /content$/, @sigarray))
+ {
+ $sigtype = 'csig';
+ }
+ return $self->{sigtype} = $sigtype;
+}
+
+# Return whether this file is configured to use stored
+# signature values from the .consign file.
+sub stored
+{
+ if (!defined $_[0]->{stored})
+ {
+ $_[0]->{stored} = grep($_ eq "stored-content", $_[0]->sigarray);
+ }
+ return $_[0]->{stored};
+}
+
+# Generic entry (file or directory) handling.
+# This is an empty subclass for nodes that haven't
+# quite decided whether they're files or dirs.
+# Use file methods until someone blesses them one way or the other.
+package entry;
+
+use vars qw( @ISA );
+
+BEGIN { @ISA = qw(file) }
+
+# File utilities
+package futil;
+
+# Install one file as another.
+# Links them if possible (hard link), otherwise copies.
+# Don't ask why, but the source is a path, the tgt is a file obj.
+sub install
+{
+ my ($sp, $tgt) = @_;
+ my ($tp) = $tgt->path;
+ return 1 if $tp eq $sp;
+ return 1 if eval { link($sp, $tp) };
+ unlink($tp);
+ if (!futil::mkdir($tgt->{dir}))
+ {
+ return undef;
+ }
+ return 1 if eval { link($sp, $tp) };
+ futil::copy($sp, $tp);
+}
+
+# Copy one file to another. Arguments are actual file names.
+# Returns undef on failure. Preserves mtime and mode.
+sub copy
+{
+ my ($sp, $tp) = @_;
+ my ($mode, $length, $atime, $mtime) = (stat($sp))[2, 7, 8, 9];
+
+ # Use Perl standard library module for file copying, which handles
+ # binary copies. <schwarze@isa.de> 1998-06-18
+ if (!File::Copy::copy($sp, $tp))
+ {
+ warn qq($0: can\'t install "$sp" to "$tp" ($!)\n); #'
+ return undef;
+ }
+
+ # The file has been created, so try both the chmod and utime,
+ # first making sure the copy is writable (because permissions
+ # affect the ability to modify file times on some operating
+ # systems), and then changing permissions back if necessary.
+ my $ret = 1;
+ my $wmode = $mode | 0700;
+ if (!chmod $wmode, $tp)
+ {
+ warn qq($0: can\'t set mode $wmode on file "$tp" ($!)\n); #'
+ $ret = undef;
+ }
+ if (!utime $atime, $mtime, $tp)
+ {
+ warn qq($0: can\'t set modification time for file "$tp" ($!)\n); #'
+ $ret = undef;
+ }
+ if ($mode != $wmode && !chmod $mode, $tp)
+ {
+ warn qq($0: can\'t set mode $mode on file "$tp" ($!)\n); #'
+ $ret = undef;
+ }
+ return $ret;
+}
+
+# Ensure that the specified directory exists.
+# Aborts on failure.
+sub mkdir
+{
+ return 1 if $_[0]->{'exists'};
+ if (!futil::mkdir($_[0]->{dir}))
+ { # Recursively make parent.
+ return undef;
+ }
+ my ($path) = $_[0]->path;
+ if (!-d $path && !mkdir($path, 0777))
+ {
+ warn qq($0: can't create directory $path ($!).\n); #'
+ return undef;
+ }
+ $_[0]->{'exists'} = 1;
+}
+
+# Signature package.
+package sig::hash;
+
+use vars qw( $called );
+
+sub init
+{
+ my ($dir) = @_;
+ my ($consign) = $dir->prefix . ".consign";
+ my ($dhash) = $dir->{consign} = {};
+ if (-f $consign)
+ {
+ open(CONSIGN, $consign) || die ("$0: can't open $consign ($!)\n");
+ while (<CONSIGN>)
+ {
+ chop;
+ my ($file, $sig) = split (/:/, $_);
+ $dhash->{$file} = $sig;
+ }
+ close(CONSIGN);
+ }
+ $dhash;
+}
+
+# Read the hash entry for a particular file.
+sub in
+{
+ my ($dir) = $_[0]->{dir};
+ ($dir->{consign} || init($dir))->{$_[0]->{entry}};
+}
+
+# Write the hash entry for a particular file.
+sub out
+{
+ my ($file, $sig) = @_;
+ my ($dir) = $file->{dir};
+ ($dir->{consign} || init($dir))->{$file->{entry}} = $sig;
+ $sig::hash::dirty{$dir} = $dir;
+}
+
+# Eliminate the hash entry for a particular file.
+sub clear
+{
+ my ($file) = @_;
+ my ($dir) = $file->{dir};
+ delete $dir->{consign}->{$file->{entry}} if $dir->{consign};
+ $sig::hash::dirty{$dir} = $dir;
+}
+
+# Flush hash entries. Called at end or via ^C interrupt.
+sub END
+{
+ return if $called++; # May be called twice.
+ close(CONSIGN); # in case this came in via ^C.
+ my $dir;
+ for $dir (values %sig::hash::dirty)
+ {
+ my ($consign) = $dir->prefix . ".consign";
+ my ($constemp) = $consign . ".$$";
+ if (!open(CONSIGN, ">$constemp"))
+ {
+ die ("$0: can't create $constemp ($!)\n");
+ }
+ my ($entry, $sig);
+ while (($entry, $sig) = each %{$dir->{consign}})
+ {
+ if (!print CONSIGN "$entry:$sig\n")
+ {
+ die ("$0: error writing to $constemp ($!)\n");
+ }
+ }
+ close(CONSIGN);
+ if (!rename($constemp, $consign))
+ {
+ if (futil::copy($constemp, $consign))
+ {
+ unlink($constemp);
+ }
+ else
+ {
+ die ("$0: couldn't rename or copy $constemp to $consign "
+ . "($!)\n");
+ }
+ }
+ }
+}
+
+# Derived file caching.
+package cache;
+
+# Find a file in the cache. Return non-null if the file is in the cache.
+sub in
+{
+ return undef unless $param::cache;
+ my ($file, $sig) = @_;
+
+ # Add the path to the signature, to make it unique.
+ $sig = 'sig'->collect($sig, $file->path) unless $param::mixtargets;
+ my ($dir) = substr($sig, 0, 1);
+ my ($cp) = File::Spec->catfile($param::cache, $dir, $sig);
+ return -f $cp && futil::install($cp, $file);
+}
+
+# Try to flush a file to the cache, if not already there.
+# If it doesn't make it out, due to an error, then that doesn't
+# really matter.
+sub out
+{
+ return unless $param::cache;
+ my ($file, $sig) = @_;
+
+ # Add the path to the signature, to make it unique.
+ $sig = 'sig'->collect($sig, $file->path) unless $param::mixtargets;
+ my ($dir) = substr($sig, 0, 1);
+ my ($sp) = $file->path;
+ my ($cp) = File::Spec->catfile($param::cache, $dir, $sig);
+ my ($cdir) = File::Spec->catfile($param::cache, $dir);
+ if (!-d $cdir)
+ {
+ mkdir($cdir, 0777)
+ || die ("$0: can't create cache directory $cdir ($!).\n");
+ }
+ elsif (-f $cp)
+ {
+
+ # Already cached: try to use that instead, to save space.
+ # This can happen if the -cs option is used on a previously
+ # uncached build, or if two builds occur simultaneously.
+ my ($lp) = ".$sig";
+ unlink($lp);
+ return if !eval { link($cp, $lp) };
+ rename($lp, $sp);
+
+ # Unix98 says, "If the old argument and the new argument both
+ # [refer] to the same existing file, the rename() function
+ # returns successfully and performs no other action." So, if
+ # $lp and $sp are links (i.e., $cp and $sp are links), $lp is
+ # left, and we must unlink it ourselves. If the rename failed
+ # for any reason, it is also good form to unlink the temporary
+ # $lp. Otherwise $lp no longer exists and, barring some race,
+ # the unlink fails silently.
+ unlink($lp);
+ return;
+ }
+
+ return if eval { link($sp, $cp) };
+ return if !-f $sp; # if nothing to cache.
+ if (futil::copy($sp, "$cp.new"))
+ {
+ rename("$cp.new", $cp);
+ }
+}
+
+# Generic signature handling package.
+# This handles the higher-layer distinction between content and build
+# signatures, relying on an underlying calculation package like
+# "sig::md5"" to provide the signature values themselves.
+package sig;
+
+use vars qw( @ISA );
+
+# Select the underlying package to be used for signature calculation.
+# We play a few namespace games here. Specifically, we append
+# "sig::" to the beginning of the subclass we're passed. Then,
+# if the package ends in "::debug", we actually subclass the
+# "sig::debug" package and as a wrapper around the underlying
+# (e.g.) "sig::md5" package that's doing the real calculation.
+sub select
+{
+ my ($package, $subclass) = @_;
+ my $p = $package . "::" . $subclass;
+ my $sigpkg = $p;
+ if ($p =~ /(.*)::debug$/)
+ {
+ $sigpkg = $1;
+ $p = 'sig::debug';
+ }
+ @ISA = ($p);
+ $p->init($sigpkg);
+};
+
+# Set or return the build signature of a file.
+# This is computed elsewhere and passed in to us.
+sub bsig
+{
+ my ($self, $file, $sig) = @_;
+ if (defined $sig)
+ {
+ $file->{'bsig'} = $sig;
+ $self->set($file);
+ }
+ elsif (!defined $file->{'bsig'})
+ {
+ $file->{'bsig'} = '';
+ }
+ $file->{'bsig'};
+}
+
+# Determine the content signature of a file.
+# This also sets the .consign entry unless the file is in a
+# repository; we don't write into repositories, only read from them.
+sub csig
+{
+ my ($self, $file) = @_;
+ if (!$file->{'csig'})
+ {
+ $file->{'csig'} = $self->srcsig($file->path);
+ $self->set($file) if !$file->is_on_rpath;
+ }
+ $_[1]->{'csig'};
+}
+
+# Determine the current signature of an already-existing or
+# non-existant file. Unless a specific signature type (bsig
+# or csig) is requested, this consults the file's signature
+# array to decide whether to return content or build signature,
+# and whether to use a cached value from a .consign file.
+sub signature
+{
+ my ($self, $file, $sigtype) = @_;
+ $sigtype = $file->sigtype if !$sigtype;
+
+ #open(TTY, ">/dev/tty");
+ #print TTY $file->path, ": $sigtype\n";
+ #close(TTY);
+ my ($path) = $file->path;
+ my ($time) = (stat($path))[9];
+ if ($time)
+ {
+ if ($file->{$sigtype})
+ {
+ return $file->{$sigtype};
+ }
+ if ($file->is_on_rpath || $file->stored)
+ {
+ if ('sig'->fetch($file) && $file->{$sigtype})
+ {
+ if ($file->{'sigtime'} == $time
+ || !$param::rep_sig_times_ok && $file->is_on_rpath)
+ {
+ return $file->{$sigtype};
+ }
+ }
+ $file->{$sigtype} = undef;
+ }
+ if ($file->is_on_rpath || !File::Spec->file_name_is_absolute($path))
+ {
+ my $sig = '';
+ if ($sigtype eq 'bsig') { $sig = $self->bsig($file); }
+ elsif ($sigtype eq 'csig') { $sig = $self->csig($file); }
+ return $sig;
+ }
+
+ # This file is not in a repository or under the local directory
+ # structure. In the canonical case, it's a utility that will be
+ # executed by a command. Historically, Cons has returned the
+ # name of the command concatenated with the modification time.
+ # Note that this is *not* the path ("cc" not "/bin/cc"), so it
+ # would lose in the unlikely event that a different copy of the
+ # utility was used that happened to have the same modification
+ # time (due to living in a different directory on the PATH, for
+ # example). The obvious "fix" of using the path like so, however:
+ # return $path . $time;
+ # is wrong. In a multi-machine build environment, different
+ # systems may have the same utility in different locations (due
+ # to different NFS mount points, for example), which would
+ # cause a lot of unnecessary builds if we used the full path.
+ # A better solution to strengthen this signature would be to
+ # also concatenate the size of the file, but that would cause
+ # unnecessary rebuilds when coming from .consign files that used
+ # the old scheme. All of which is to merely explain why we're
+ # leaving this as it has been, but documenting it here in case
+ # there's reason to change it in the future.
+ return $file->{entry} . $time;
+ }
+ return $file->{$sigtype} = '';
+}
+
+sub bsignature
+{
+ my ($self, $file) = @_;
+ my ($path) = $file->path;
+ my ($time) = (stat($path))[9];
+ if ($time)
+ {
+ if ($file->{'bsig'})
+ {
+ return $file->{'bsig'};
+ }
+ if ('sig'->fetch($file, 'bsig') && $file->{'bsig'})
+ {
+ if ($file->{'sigtime'} == $time
+ || !$param::rep_sig_times_ok && $file->is_on_rpath)
+ {
+ return $file->{'bsig'};
+ }
+ }
+ if ($file->is_on_rpath || !File::Spec->file_name_is_absolute($path))
+ {
+ return $self->bsig($file);
+ }
+ return $path . $time;
+ }
+ return $file->{'bsig'} = '';
+}
+
+# Invalidate a file's signature, also clearing its .consign entry.
+sub invalidate
+{
+ my ($self, $file) = @_;
+ delete $file->{'sigtime'};
+ delete $file->{'bsig'};
+ delete $file->{'csig'};
+ sig::hash::clear($file);
+}
+
+# Store the signature for a file.
+sub set
+{
+ my ($self, $file) = @_;
+ my $sig = (stat($file->path))[9];
+ $sig .= " " . ($file->{'bsig'} || '-');
+ $sig .= " " . $file->{'csig'} if $file->{'csig'};
+ sig::hash::out($file, $sig);
+}
+
+# Fetch the signature(s) for a file.
+# Returns whether there was a signature to fetch.
+sub fetch
+{
+ my ($self, $file, @kw) = @_;
+ @kw = ('bsig', 'csig') if !@kw;
+ my $sig = sig::hash::in($file) || '';
+ my ($sigtime, $bsig, $csig) = split (/ /, $sig);
+ $file->{'sigtime'} = $sigtime;
+ $file->{'bsig'} = $bsig || '' if grep($_ eq 'bsig', @kw);
+ $file->{'csig'} = $csig || '' if grep($_ eq 'csig', @kw);
+ $file->{'bsig'} = '' if $file->{'bsig'} eq '-';
+ return $sig ne '';
+}
+
+# MD5-based signature package.
+package sig::md5;
+
+use vars qw( $md5 );
+
+# Initialize MD5 signature calculation by finding an appropriate
+# module and creating the proper object.
+sub init
+{
+ my $self = shift;
+ my @md5_modules = qw(Digest::MD5 MD5 Digest::Perl::MD5);
+
+ # We used to find the right module more simply, using $_ as the
+ # loop iterator and just doing:
+ #
+ # eval "use $_";
+ # $module = $_, $last if ! $@;
+ #
+ # in the loop. Empirically, though, this doesn't pass back the
+ # right value in $module on some ActiveState versions. (Maybe
+ # it's something to do with the eval in a for loop, I dunno.)
+ # Work around it by using $_ to pass the value out of the loop,
+ # which seems to work everywhere.
+ my $module;
+ for $module (@md5_modules)
+ {
+ eval "use $module";
+ $_ = $module, last if !$@;
+ }
+ $module = $_;
+ die "Cannot find any MD5 module from: @md5_modules" if $@;
+
+ $md5 = new $module;
+}
+
+# Is the provided signature equal to the signature of the current
+# instantiation of the target (and does the target exist)?
+sub current
+{
+ my ($self, $file, $sig, $sigtype) = @_;
+ $self->bsignature($file) eq $sig;
+}
+
+# Return an aggregate signature for a list of signature values.
+sub collect
+{
+ my ($self, @sigs) = @_;
+
+ # The following sequence is faster than calling the hex interface.
+ $md5->reset();
+ $md5->add(join ('', $param::salt, @sigs));
+ unpack("H*", $md5->digest());
+}
+
+# Directly compute a file signature as the MD5 checksum of the
+# bytes in the file.
+sub srcsig
+{
+ my ($self, $path) = @_;
+ $md5->reset();
+ open(FILE, $path) || return '';
+ binmode(FILE);
+ $md5->addfile(\*FILE);
+ close(FILE);
+ unpack("H*", $md5->digest());
+}
+
+# Compute the signature of a command string.
+# For MD5, this is just the string itself, since MD5 will condense
+# the string contents into the ultimate signature. Other signature
+# schemes may need to figure this out differently.
+sub cmdsig
+{
+ my ($self, $sig) = @_;
+ return $sig;
+}
+
+# Generic debug package for signature calculation.
+# Because of the way we're called by sig::select() and then use
+# the specified value to set up @ISA, this package is essentially a
+# factory that creates packages like sig::md5::debug, etc., on the fly.
+package sig::debug;
+
+use vars qw( @ISA $sigpkg $outfh );
+
+local *FH;
+
+sub init
+{
+ my $self = shift;
+ $sigpkg = shift;
+ @ISA = ($sigpkg);
+ $sigpkg->init();
+ my $file = $ENV{CONS_SIG_DEBUG};
+ if ($file)
+ {
+ if (!open(FH, ">$file"))
+ {
+ die "Cannot open $file: $!";
+ }
+ $outfh = \*FH;
+ }
+ else
+ {
+ $outfh = \*STDOUT;
+ }
+}
+
+sub current
+{
+ my ($self, $file, $sig, $sigtype) = @_;
+ my $fsig = $self->bsignature($file);
+ my $sub = "${sigpkg}::current";
+ my $sep = "\n" . ' ' x (length($sub) + 1 - 3);
+ print $outfh "$sub(|$fsig|${sep}eq |$sig|)\n";
+ return $fsig eq $sig;
+}
+
+sub collect
+{
+ my ($self, @sigs) = @_;
+ my $sig = $sigpkg->collect(@sigs);
+ my $sub = "${sigpkg}::collect";
+ my $sep = ",\n" . ' ' x (length($sub) + 1);
+ my $buf = join ($sep, @sigs);
+ $buf = $param::salt . $sep . $buf if $param::salt;
+ print $outfh "$sub($buf)\n\t=> |$sig|\n";
+ return $sig;
+}
+
+sub srcsig
+{
+ my ($self, $path) = @_;
+ my $sig = $sigpkg->srcsig($path);
+ print $outfh "${sigpkg}::srcsig($path)\n\t=> |$sig|\n";
+ return $sig;
+}
+
+__END__;
+
+=head1 NAME
+
+Cons - A Software Construction System
+
+=head1 DESCRIPTION
+
+A guide and reference for version __VERSION____REVISION__
+
+Copyright (c) 1996-2001 Free Software Foundation, Inc.
+
+This program is free software; you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation; either version 2 of the License, or
+(at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; see the file COPYING. If not, write to
+the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+Boston, MA 02111-1307, USA.
+
+=head1 Introduction
+
+B<Cons> is a system for constructing, primarily, software, but is quite
+different from previous software construction systems. Cons was designed
+from the ground up to deal easily with the construction of software spread
+over multiple source directories. Cons makes it easy to create build scripts
+that are simple, understandable and maintainable. Cons ensures that complex
+software is easily and accurately reproducible.
+
+Cons uses a number of techniques to accomplish all of this. Construction
+scripts are just Perl scripts, making them both easy to comprehend and very
+flexible. Global scoping of variables is replaced with an import/export
+mechanism for sharing information between scripts, significantly improving
+the readability and maintainability of each script. B<Construction
+environments> are introduced: these are Perl objects that capture the
+information required for controlling the build process. Multiple
+environments are used when different semantics are required for generating
+products in the build tree. Cons implements automatic dependency analysis
+and uses this to globally sequence the entire build. Variant builds are
+easily produced from a single source tree. Intelligent build subsetting is
+possible, when working on localized changes. Overrides can be setup to
+easily override build instructions without modifying any scripts. MD5
+cryptographic B<signatures> are associated with derived files, and are used
+to accurately determine whether a given file needs to be rebuilt.
+
+While offering all of the above, and more, Cons remains simple and easy to
+use. This will, hopefully, become clear as you read the remainder of this
+document.
+
+
+=head1 Why Cons? Why not Make?
+
+Cons is a B<make> replacement. In the following paragraphs, we look at a few
+of the undesirable characteristics of make--and typical build environments
+based on make--that motivated the development of Cons.
+
+=head2 Build complexity
+
+Traditional make-based systems of any size tend to become quite complex. The
+original make utility and its derivatives have contributed to this tendency
+in a number of ways. Make is not good at dealing with systems that are
+spread over multiple directories. Various work-arounds are used to overcome
+this difficulty; the usual choice is for make to invoke itself recursively
+for each sub-directory of a build. This leads to complicated code, in which
+it is often unclear how a variable is set, or what effect the setting of a
+variable will have on the build as a whole. The make scripting language has
+gradually been extended to provide more possibilities, but these have
+largely served to clutter an already overextended language. Often, builds
+are done in multiple passes in order to provide appropriate products from
+one directory to another directory. This represents a further increase in
+build complexity.
+
+
+=head2 Build reproducibility
+
+The bane of all makes has always been the correct handling of
+dependencies. Most often, an attempt is made to do a reasonable job of
+dependencies within a single directory, but no serious attempt is made to do
+the job between directories. Even when dependencies are working correctly,
+make's reliance on a simple time stamp comparison to determine whether a
+file is out of date with respect to its dependents is not, in general,
+adequate for determining when a file should be rederived. If an external
+library, for example, is rebuilt and then ``snapped'' into place, the
+timestamps on its newly created files may well be earlier than the last
+local build, since it was built before it became visible.
+
+
+=head2 Variant builds
+
+Make provides only limited facilities for handling variant builds. With the
+proliferation of hardware platforms and the need for debuggable
+vs. optimized code, the ability to easily create these variants is
+essential. More importantly, if variants are created, it is important to
+either be able to separate the variants or to be able to reproduce the
+original or variant at will. With make it is very difficult to separate the
+builds into multiple build directories, separate from the source. And if
+this technique isn't used, it's also virtually impossible to guarantee at
+any given time which variant is present in the tree, without resorting to a
+complete rebuild.
+
+
+=head2 Repositories
+
+Make provides only limited support for building software from code that
+exists in a central repository directory structure. The VPATH feature of
+GNU make (and some other make implementations) is intended to provide this,
+but doesn't work as expected: it changes the path of target file to the
+VPATH name too early in its analysis, and therefore searches for all
+dependencies in the VPATH directory. To ensure correct development builds,
+it is important to be able to create a file in a local build directory and
+have any files in a code repository (a VPATH directory, in make terms) that
+depend on the local file get rebuilt properly. This isn't possible with
+VPATH, without coding a lot of complex repository knowledge directly into
+the makefiles.
+
+
+=head1 Keeping it simple
+
+A few of the difficulties with make have been cited above. In this and
+subsequent sections, we shall introduce Cons and show how these issues are
+addressed.
+
+=head2 Perl scripts
+
+Cons is Perl-based. That is, Cons scripts--F<Conscript> and F<Construct>
+files, the equivalent to F<Makefile> or F<makefile>--are all written in
+Perl. This provides an immediate benefit: the language for writing scripts
+is a familiar one. Even if you don't happen to be a Perl programmer, it
+helps to know that Perl is basically just a simple declarative language,
+with a well-defined flow of control, and familiar semantics. It has
+variables that behave basically the way you would expect them to,
+subroutines, flow of control, and so on. There is no special syntax
+introduced for Cons. The use of Perl as a scripting language simplifies
+the task of expressing the appropriate solution to the often complex
+requirements of a build.
+
+
+=head2 Hello, World!
+
+To ground the following discussion, here's how you could build the B<Hello,
+World!> C application with Cons:
+
+
+
+ $env = new cons();
+ Program $env 'hello', 'hello.c';
+
+If you install this script in a directory, naming the script F<Construct>,
+and create the F<hello.c> source file in the same directory, then you can
+type C<cons hello> to build the application:
+
+
+
+ % cons hello
+ cc -c hello.c -o hello.o
+ cc -o hello hello.o
+
+
+=head2 Construction environments
+
+A key simplification of Cons is the idea of a B<construction environment>. A
+construction environment is an B<object> characterized by a set of key/value
+pairs and a set of B<methods>. In order to tell Cons how to build something,
+you invoke the appropriate method via an appropriate construction
+environment. Consider the following example:
+
+
+
+ $env = new cons(
+ CC => 'gcc',
+ LIBS => 'libworld.a'
+ );
+
+ Program $env 'hello', 'hello.c';
+
+In this case, rather than using the default construction environment, as is,
+we have overridden the value of C<CC> so that the GNU C Compiler equivalent
+is used, instead. Since this version of B<Hello, World!> requires a library,
+F<libworld.a>, we have specified that any program linked in this environment
+should be linked with that library. If the library exists already, well and
+good, but if not, then we'll also have to include the statement:
+
+
+
+ Library $env 'libworld', 'world.c';
+
+Now if you type C<cons hello>, the library will be built before the program
+is linked, and, of course, C<gcc> will be used to compile both modules:
+
+
+
+ % cons hello
+ gcc -c hello.c -o hello.o
+ gcc -c world.c -o world.o
+ ar r libworld.a world.o
+ ar: creating libworld.a
+ ranlib libworld.a
+ gcc -o hello hello.o libworld.a
+
+
+=head2 Automatic and complete dependency analysis
+
+With Cons, dependencies are handled automatically. Continuing the previous
+example, note that when we modify F<world.c>, F<world.o> is recompiled,
+F<libworld.a> recreated, and F<hello> relinked:
+
+
+
+ % vi world.c
+ [EDIT]
+ % cons hello
+ gcc -c world.c -o world.o
+ ar r libworld.a world.o
+ ar: creating libworld.a
+ ranlib libworld.a
+ gcc -o hello hello.o libworld.a
+
+This is a relatively simple example: Cons ``knows'' F<world.o> depends upon
+F<world.c>, because the dependency is explicitly set up by the C<Library>
+method. It also knows that F<libworld.a> depends upon F<world.o> and that
+F<hello> depends upon F<libworld.a>, all for similar reasons.
+
+Now it turns out that F<hello.c> also includes the interface definition
+file, F<world.h>:
+
+
+
+ % emacs world.h
+ [EDIT]
+ % cons hello
+ gcc -c hello.c -o hello.o
+ gcc -o hello hello.o libworld.a
+
+How does Cons know that F<hello.c> includes F<world.h>, and that F<hello.o>
+must therefore be recompiled? For now, suffice it to say that when
+considering whether or not F<hello.o> is up-to-date, Cons invokes a scanner
+for its dependency, F<hello.c>. This scanner enumerates the files included
+by F<hello.c> to come up with a list of further dependencies, beyond those
+made explicit by the Cons script. This process is recursive: any files
+included by included files will also be scanned.
+
+Isn't this expensive? The answer is--it depends. If you do a full build of a
+large system, the scanning time is insignificant. If you do a rebuild of a
+large system, then Cons will spend a fair amount of time thinking about it
+before it decides that nothing has to be done (although not necessarily more
+time than make!). The good news is that Cons makes it very easy to
+intelligently subset your build, when you are working on localized changes.
+
+
+=head2 Automatic global build sequencing
+
+Because Cons does full and accurate dependency analysis, and does this
+globally, for the entire build, Cons is able to use this information to take
+full control of the B<sequencing> of the build. This sequencing is evident
+in the above examples, and is equivalent to what you would expect for make,
+given a full set of dependencies. With Cons, this extends trivially to
+larger, multi-directory builds. As a result, all of the complexity involved
+in making sure that a build is organized correctly--including multi-pass
+hierarchical builds--is eliminated. We'll discuss this further in the next
+sections.
+
+=head1 Building large trees--still just as simple
+
+
+=head2 A hierarchy of build scripts
+
+A larger build, in Cons, is organized by creating a hierarchy of B<build
+scripts>. At the top of the tree is a script called F<Construct>. The rest
+of the scripts, by convention, are each called F<Conscript>. These scripts
+are connected together, very simply, by the C<Build>, C<Export>, and
+C<Import> commands.
+
+
+=head2 The Build command
+
+The C<Build> command takes a list of F<Conscript> file names, and arranges
+for them to be included in the build. For example:
+
+ Build qw(
+ drivers/display/Conscript
+ drivers/mouse/Conscript
+ parser/Conscript
+ utilities/Conscript
+ );
+
+This is a simple two-level hierarchy of build scripts: all the subsidiary
+F<Conscript> files are mentioned in the top-level F<Construct> file. Notice
+that not all directories in the tree necessarily have build scripts
+associated with them.
+
+This could also be written as a multi-level script. For example, the
+F<Construct> file might contain this command:
+
+ Build qw(
+ parser/Conscript
+ drivers/Conscript
+ utilities/Conscript
+ );
+
+and the F<Conscript> file in the F<drivers> directory might contain this:
+
+ Build qw(
+ display/Conscript
+ mouse/Conscript
+ );
+
+Experience has shown that the former model is a little easier to understand,
+since the whole construction tree is laid out in front of you, at the
+top-level. Hybrid schemes are also possible. A separately maintained
+component that needs to be incorporated into a build tree, for example,
+might hook into the build tree in one place, but define its own construction
+hierarchy.
+
+By default, Cons does not change its working directory to the directory
+containing a subsidiary F<Conscript> file it is including. This behavior
+can be enabled for a build by specifying, in the top-level F<Construct>
+file:
+
+ Conscript_chdir 1;
+
+When enabled, Cons will change to the subsidiary F<Conscript> file's
+containing directory while reading in that file, and then change back
+to the top-level directory once the file has been processed.
+
+It is expected that this behavior will become the default in some future
+version of Cons. To prepare for this transition, builds that expect
+Cons to remain at the top of the build while it reads in a subsidiary
+F<Conscript> file should explicitly disable this feature as follows:
+
+ Conscript_chdir 0;
+
+
+=head2 Relative, top-relative, and absolute file names
+
+You may have noticed that the file names specified to the Build command are
+relative to the location of the script it is invoked from. This is generally
+true for other filename arguments to other commands, too, although we might
+as well mention here that if you begin a file name with a hash mark, ``#'',
+then that file is interpreted relative to the top-level directory (where the
+F<Construct> file resides). And, not surprisingly, if you begin it with ``/'',
+then it is considered to be an absolute pathname. This is true even on
+systems which use a back slash rather than a forward slash to name absolute
+paths.
+
+(There is another file prefix, ``!'', that is interpreted specially by
+Cons. See discussion of the C<Link> command, below, for details.)
+
+
+=head2 Using modules in build scripts
+
+You may pull modules into each F<Conscript> file using the normal Perl
+C<use> or C<require> statements:
+
+ use English;
+ require My::Module;
+
+Each C<use> or C<require> only affects the one F<Conscript> file in which
+it appears. To use a module in multiple F<Conscript> files, you must
+put a C<use> or C<require> statement in each one that needs the module.
+
+
+=head2 Scope of variables
+
+The top-level F<Construct> file and all F<Conscript> files begin life in
+a common, separate Perl package. B<Cons> controls the symbol table for
+the package so that, the symbol table for each script is empty, except
+for the F<Construct> file, which gets some of the command line arguments.
+All of the variables that are set or used, therefore, are set by the
+script itself--not by some external script.
+
+Variables can be explicitly B<imported> by a script from its parent
+script. To import a variable, it must have been B<exported> by the parent
+and initialized (otherwise an error will occur).
+
+
+=head2 The Export command
+
+The C<Export> command is used as in the following example:
+
+ $env = new cons();
+ $INCLUDE = "#export/include";
+ $LIB = "#export/lib";
+ Export qw( env INCLUDE LIB );
+ Build qw( util/Conscript );
+
+The values of the simple variables mentioned in the C<Export> list will be
+squirreled away by any subsequent C<Build> commands. The C<Export> command
+will only export Perl B<scalar> variables, that is, variables whose name
+begins with C<$>. Other variables, objects, etc. can be exported by
+reference--but all scripts will refer to the same object, and this object
+should be considered to be read-only by the subsidiary scripts and by the
+original exporting script. It's acceptable, however, to assign a new value
+to the exported scalar variable--that won't change the underlying variable
+referenced. This sequence, for example, is OK:
+
+ $env = new cons();
+ Export qw( env INCLUDE LIB );
+ Build qw( util/Conscript );
+ $env = new cons(CFLAGS => '-O');
+ Build qw( other/Conscript );
+
+It doesn't matter whether the variable is set before or after the C<Export>
+command. The important thing is the value of the variable at the time the
+C<Build> command is executed. This is what gets squirreled away. Any
+subsequent C<Export> commands, by the way, invalidate the first: you must
+mention all the variables you wish to export on each C<Export> command.
+
+
+=head2 The Import command
+
+Variables exported by the C<Export> command can be imported into subsidiary
+scripts by the C<Import> command. The subsidiary script always imports
+variables directly from the superior script. Consider this example:
+
+ Import qw( env INCLUDE );
+
+This is only legal if the parent script exported both C<$env> and
+C<$INCLUDE>. It also must have given each of these variables values. It is
+OK for the subsidiary script to only import a subset of the exported
+variables (in this example, C<$LIB>, which was exported by the previous
+example, is not imported).
+
+All the imported variables are automatically re-exported, so the sequence:
+
+ Import qw ( env INCLUDE );
+ Build qw ( beneath-me/Conscript );
+
+will supply both C<$env> and C<$INCLUDE> to the subsidiary file. If only
+C<$env> is to be exported, then the following will suffice:
+
+ Import qw ( env INCLUDE );
+ Export qw ( env );
+ Build qw ( beneath-me/Conscript );
+
+Needless to say, the variables may be modified locally before invoking
+C<Build> on the subsidiary script.
+
+
+=head2 Build script evaluation order
+
+The only constraint on the ordering of build scripts is that superior
+scripts are evaluated before their inferior scripts. The top-level
+F<Construct> file, for instance, is evaluated first, followed by any
+inferior scripts. This is all you really need to know about the evaluation
+order, since order is generally irrelevant. Consider the following C<Build>
+command:
+
+ Build qw(
+ drivers/display/Conscript
+ drivers/mouse/Conscript
+ parser/Conscript
+ utilities/Conscript
+ );
+
+We've chosen to put the script names in alphabetical order, simply because
+that's the most convenient for maintenance purposes. Changing the order will
+make no difference to the build.
+
+
+=head1 A Model for sharing files
+
+
+=head2 Some simple conventions
+
+In any complex software system, a method for sharing build products needs to
+be established. We propose a simple set of conventions which are trivial to
+implement with Cons, but very effective.
+
+The basic rule is to require that all build products which need to be shared
+between directories are shared via an intermediate directory. We have
+typically called this F<export>, and, in a C environment, provided
+conventional sub-directories of this directory, such as F<include>, F<lib>,
+F<bin>, etc.
+
+These directories are defined by the top-level F<Construct> file. A simple
+F<Construct> file for a B<Hello, World!> application, organized using
+multiple directories, might look like this:
+
+ # Construct file for Hello, World!
+
+ # Where to put all our shared products.
+ $EXPORT = '#export';
+
+ Export qw( CONS INCLUDE LIB BIN );
+
+ # Standard directories for sharing products.
+ $INCLUDE = "$EXPORT/include";
+ $LIB = "$EXPORT/lib";
+ $BIN = "$EXPORT/bin";
+
+ # A standard construction environment.
+ $CONS = new cons (
+ CPPPATH => $INCLUDE, # Include path for C Compilations
+ LIBPATH => $LIB, # Library path for linking programs
+ LIBS => '-lworld', # List of standard libraries
+ );
+
+ Build qw(
+ hello/Conscript
+ world/Conscript
+ );
+
+The F<world> directory's F<Conscript> file looks like this:
+
+ # Conscript file for directory world
+ Import qw( CONS INCLUDE LIB );
+
+ # Install the products of this directory
+ Install $CONS $LIB, 'libworld.a';
+ Install $CONS $INCLUDE, 'world.h';
+
+ # Internal products
+ Library $CONS 'libworld.a', 'world.c';
+
+and the F<hello> directory's F<Conscript> file looks like this:
+
+ # Conscript file for directory hello
+ Import qw( CONS BIN );
+
+ # Exported products
+ Install $CONS $BIN, 'hello';
+
+ # Internal products
+ Program $CONS 'hello', 'hello.c';
+
+To construct a B<Hello, World!> program with this directory structure, go to
+the top-level directory, and invoke C<cons> with the appropriate
+arguments. In the following example, we tell Cons to build the directory
+F<export>. To build a directory, Cons recursively builds all known products
+within that directory (only if they need rebuilding, of course). If any of
+those products depend upon other products in other directories, then those
+will be built, too.
+
+ % cons export
+ Install world/world.h as export/include/world.h
+ cc -Iexport/include -c hello/hello.c -o hello/hello.o
+ cc -Iexport/include -c world/world.c -o world/world.o
+ ar r world/libworld.a world/world.o
+ ar: creating world/libworld.a
+ ranlib world/libworld.a
+ Install world/libworld.a as export/lib/libworld.a
+ cc -o hello/hello hello/hello.o -Lexport/lib -lworld
+ Install hello/hello as export/bin/hello
+
+
+=head2 Clean, understandable, location-independent scripts
+
+You'll note that the two F<Conscript> files are very clean and
+to-the-point. They simply specify products of the directory and how to build
+those products. The build instructions are minimal: they specify which
+construction environment to use, the name of the product, and the name of
+the inputs. Note also that the scripts are location-independent: if you wish
+to reorganize your source tree, you are free to do so: you only have to
+change the F<Construct> file (in this example), to specify the new locations
+of the F<Conscript> files. The use of an export tree makes this goal easy.
+
+Note, too, how Cons takes care of little details for you. All the F<export>
+directories, for example, were made automatically. And the installed files
+were really hard-linked into the respective export directories, to save
+space and time. This attention to detail saves considerable work, and makes
+it even easier to produce simple, maintainable scripts.
+
+
+=head1 Separating source and build trees
+
+It's often desirable to keep any derived files from the build completely
+separate from the source files. This makes it much easier to keep track of
+just what is a source file, and also makes it simpler to handle B<variant>
+builds, especially if you want the variant builds to co-exist.
+
+
+=head2 Separating build and source directories using the Link command
+
+Cons provides a simple mechanism that handles all of these requirements. The
+C<Link> command is invoked as in this example:
+
+ Link 'build' => 'src';
+
+The specified directories are ``linked'' to the specified source
+directory. Let's suppose that you setup a source directory, F<src>, with the
+sub-directories F<world> and F<hello> below it, as in the previous
+example. You could then substitute for the original build lines the
+following:
+
+ Build qw(
+ build/world/Conscript
+ build/hello/Conscript
+ );
+
+Notice that you treat the F<Conscript> file as if it existed in the build
+directory. Now if you type the same command as before, you will get the
+following results:
+
+ % cons export
+ Install build/world/world.h as export/include/world.h
+ cc -Iexport/include -c build/hello/hello.c -o build/hello/hello.o
+ cc -Iexport/include -c build/world/world.c -o build/world/world.o
+ ar r build/world/libworld.a build/world/world.o
+ ar: creating build/world/libworld.a
+ ranlib build/world/libworld.a
+ Install build/world/libworld.a as export/lib/libworld.a
+ cc -o build/hello/hello build/hello/hello.o -Lexport/lib -lworld
+ Install build/hello/hello as export/bin/hello
+
+Again, Cons has taken care of the details for you. In particular, you will
+notice that all the builds are done using source files and object files from
+the build directory. For example, F<build/world/world.o> is compiled from
+F<build/world/world.c>, and F<export/include/world.h> is installed from
+F<build/world/world.h>. This is accomplished on most systems by the simple
+expedient of ``hard'' linking the required files from each source directory
+into the appropriate build directory.
+
+The links are maintained correctly by Cons, no matter what you do to the
+source directory. If you modify a source file, your editor may do this ``in
+place'' or it may rename it first and create a new file. In the latter case,
+any hard link will be lost. Cons will detect this condition the next time
+the source file is needed, and will relink it appropriately.
+
+You'll also notice, by the way, that B<no> changes were required to the
+underlying F<Conscript> files. And we can go further, as we shall see in the
+next section.
+
+=head2 Explicit references to the source directory
+
+When using the C<Link> command on some operating systems or with some
+tool chains, it's sometimes useful to have a command actually use
+the path name to the source directory, not the build directory. For
+example, on systems that must copy, not "hard link," the F<src/> and
+F<build/> copies of C<Linked> files, using the F<src/> path of a file
+name might make an editor aware that a syntax error must be fixed in the
+source directory, not the build directory.
+
+You can tell Cons that you want to use the "source path" for a file by
+preceding the file name with a ``!'' (exclamation point). For example,
+if we add a ``!'' to the beginning of a source file:
+
+ Program $env "foo", "!foo.c"; # Notice initial ! on foo.c
+
+Cons will compile the target as follows:
+
+ cc -c src/foo.c -o build/foo.o
+ cc -o build/foo build/foo.o
+
+Notice that Cons has compiled the program from the the F<src/foo.c>
+source file. Without the initial ``!'', Cons would have compiled the
+program using the F<build/foo.c> path name.
+
+
+
+=head1 Variant builds
+
+
+=head2 Hello, World! for baNaNa and peAcH OS's
+
+Variant builds require just another simple extension. Let's take as an
+example a requirement to allow builds for both the baNaNa and peAcH
+operating systems. In this case, we are using a distributed file system,
+such as NFS to access the particular system, and only one or the other of
+the systems has to be compiled for any given invocation of C<cons>. Here's
+one way we could set up the F<Construct> file for our B<Hello, World!>
+application:
+
+ # Construct file for Hello, World!
+
+ die qq(OS must be specified) unless $OS = $ARG{OS};
+ die qq(OS must be "peach" or "banana")
+ if $OS ne "peach" && $OS ne "banana";
+
+ # Where to put all our shared products.
+ $EXPORT = "#export/$OS";
+
+ Export qw( CONS INCLUDE LIB BIN );
+
+ # Standard directories for sharing products.
+ $INCLUDE = "$EXPORT/include";
+ $LIB = "$EXPORT/lib";
+ $BIN = "$EXPORT/bin";
+
+ # A standard construction environment.
+ $CONS = new cons (
+ CPPPATH => $INCLUDE, # Include path for C Compilations
+ LIBPATH => $LIB, # Library path for linking programs
+ LIBS => '-lworld', # List of standard libraries
+ );
+
+ # $BUILD is where we will derive everything.
+ $BUILD = "#build/$OS";
+
+ # Tell cons where the source files for $BUILD are.
+ Link $BUILD => 'src';
+
+ Build (
+ "$BUILD/hello/Conscript",
+ "$BUILD/world/Conscript",
+ );
+
+Now if we login to a peAcH system, we can build our B<Hello, World!>
+application for that platform:
+
+ % cons export OS=peach
+ Install build/peach/world/world.h as export/peach/include/world.h
+ cc -Iexport/peach/include -c build/peach/hello/hello.c -o build/peach/hello/hello.o
+ cc -Iexport/peach/include -c build/peach/world/world.c -o build/peach/world/world.o
+ ar r build/peach/world/libworld.a build/peach/world/world.o
+ ar: creating build/peach/world/libworld.a
+ ranlib build/peach/world/libworld.a
+ Install build/peach/world/libworld.a as export/peach/lib/libworld.a
+ cc -o build/peach/hello/hello build/peach/hello/hello.o -Lexport/peach/lib -lworld
+ Install build/peach/hello/hello as export/peach/bin/hello
+
+
+=head2 Variations on a theme
+
+Other variations of this model are possible. For example, you might decide
+that you want to separate out your include files into platform dependent and
+platform independent files. In this case, you'd have to define an
+alternative to C<$INCLUDE> for platform-dependent files. Most F<Conscript>
+files, generating purely platform-independent include files, would not have
+to change.
+
+You might also want to be able to compile your whole system with debugging
+or profiling, for example, enabled. You could do this with appropriate
+command line options, such as C<DEBUG=on>. This would then be translated
+into the appropriate platform-specific requirements to enable debugging
+(this might include turning off optimization, for example). You could
+optionally vary the name space for these different types of systems, but, as
+we'll see in the next section, it's not B<essential> to do this, since Cons
+is pretty smart about rebuilding things when you change options.
+
+
+=head1 Signatures
+
+Cons uses file B<signatures> to decide if a derived file is out-of-date
+and needs rebuilding. In essence, if the contents of a file change,
+or the manner in which the file is built changes, the file's signature
+changes as well. This allows Cons to decide with certainty when a file
+needs rebuilding, because Cons can detect, quickly and reliably, whether
+any of its dependency files have been changed.
+
+
+=head2 MD5 content and build signatures
+
+Cons uses the B<MD5> (B<Message Digest 5>) algorithm to compute file
+signatures. The MD5 algorithm computes a strong cryptographic checksum
+for any given input string. Cons can, based on configuration, use two
+different MD5 signatures for a given file:
+
+The B<content signature> of a file is an MD5 checksum of the file's
+contents. Consequently, when the contents of a file change, its content
+signature changes as well.
+
+The B<build signature> of a file is a combined MD5 checksum of:
+
+=over 4
+
+the signatures of all the input files used to build the file
+
+the signatures of all dependency files discovered by source scanners
+(for example, C<.h> files)
+
+the signatures of all dependency files specified explicitly via the
+C<Depends> method)
+
+the command-line string used to build the file
+
+=back
+
+The build signature is, in effect, a digest of all the dependency
+information for the specified file. Consequently, a file's build
+signature changes whenever any part of its dependency information
+changes: a new file is added, the contents of a file on which it depends
+change, there's a change to the command line used to build the file (or
+any of its dependency files), etc.
+
+For example, in the previous section, the build signature of the
+F<world.o> file will include:
+
+=over 4
+
+the signature of the F<world.c> file
+
+the signatures of any header files that Cons detects are included,
+directly or indirectly, by F<world.c>
+
+the text of the actual command line was used to generate F<world.o>
+
+=back
+
+Similarly, the build signature of the F<libworld.a> file will include
+all the signatures of its constituents (and hence, transitively, the
+signatures of B<their> constituents), as well as the command line that
+created the file.
+
+Note that there is no need for a derived file to depend upon any
+particular F<Construct> or F<Conscript> file. If changes to these files
+affect a file, then this will be automatically reflected in its build
+signature, since relevant parts of the command line are included in the
+signature. Unrelated F<Construct> or F<Conscript> changes will have no
+effect.
+
+
+=head2 Storing signatures in .consign files
+
+Before Cons exits, it stores the calculated signatures for all of the
+files it built or examined in F<.consign> files, one per directory.
+Cons uses this stored information on later invocations to decide if
+derived files need to be rebuilt.
+
+After the previous example was compiled, the F<.consign> file in the
+F<build/peach/world> directory looked like this:
+
+ world.h:985533370 - d181712f2fdc07c1f05d97b16bfad904
+ world.o:985533372 2a0f71e0766927c0532977b0d2158981
+ world.c:985533370 - c712f77189307907f4189b5a7ab62ff3
+ libworld.a:985533374 69e568fc5241d7d25be86d581e1fb6aa
+
+After the file name and colon, the first number is a timestamp of the
+file's modification time (on UNIX systems, this is typically the number
+of seconds since January 1st, 1970). The second value is the build
+signature of the file (or ``-'' in the case of files with no build
+signature--that is, source files). The third value, if any, is the
+content signature of the file.
+
+
+=head2 Using build signatures to decide when to rebuild files
+
+When Cons is deciding whether to build or rebuild a derived file, it
+first computes the file's current build signature. If the file doesn't
+exist, it must obviously be built.
+
+If, however, the file already exists, Cons next compares the
+modification timestamp of the file against the timestamp value in
+the F<.consign> file. If the timestamps match, Cons compares the
+newly-computed build signature against the build signature in the
+F<.consign> file. If the timestamps do not match or the build
+signatures do not match, the derived file is rebuilt.
+
+After the file is built or rebuilt, Cons arranges to store the
+newly-computed build signature in the F<.consign> file when it exits.
+
+
+=head2 Signature example
+
+The use of these signatures is an extremely simple, efficient, and
+effective method of improving--dramatically--the reproducibility of a
+system.
+
+We'll demonstrate this with a simple example:
+
+ # Simple "Hello, World!" Construct file
+ $CFLAGS = '-g' if $ARG{DEBUG} eq 'on';
+ $CONS = new cons(CFLAGS => $CFLAGS);
+ Program $CONS 'hello', 'hello.c';
+
+Notice how Cons recompiles at the appropriate times:
+
+ % cons hello
+ cc -c hello.c -o hello.o
+ cc -o hello hello.o
+ % cons hello
+ cons: "hello" is up-to-date.
+ % cons DEBUG=on hello
+ cc -g -c hello.c -o hello.o
+ cc -o hello hello.o
+ % cons DEBUG=on hello
+ cons: "hello" is up-to-date.
+ % cons hello
+ cc -c hello.c -o hello.o
+ cc -o hello hello.o
+
+
+=head2 Source-file signature configuration
+
+Cons provides a C<SourceSignature> method that allows you to configure
+how the signature should be calculated for any source file when its
+signature is being used to decide if a dependent file is up-to-date.
+The arguments to the C<SourceSignature> method consist of one or more
+pairs of strings:
+
+ SourceSignature 'auto/*.c' => 'content',
+ '*' => 'stored-content';
+
+The first string in each pair is a pattern to match against derived file
+path names. The pattern is a file-globbing pattern, not a Perl regular
+expression; the pattern <*.l> will match all Lex source files. The C<*>
+wildcard will match across directory separators; the pattern C<foo/*.c>
+would match all C source files in any subdirectory underneath the C<foo>
+subdirectory.
+
+The second string in each pair contains one of the following keywords to
+specify how signatures should be calculated for source files that match
+the pattern. The available keywords are:
+
+=over 4
+
+=item content
+
+Use the content signature of the source file when calculating signatures
+of files that depend on it. This guarantees correct calculation of the
+file's signature for all builds, by telling Cons to read the contents of
+a source file to calculate its content signature each time it is run.
+
+=item stored-content
+
+Use the source file's content signature as stored in the F<.consign>
+file, provided the file's timestamp matches the cached timestamp value
+in the F<.consign> file. This optimizes performance, with the slight
+risk of an incorrect build if a source file's contents have been changed
+so quickly after its previous update that the timestamp still matches
+the stored timestamp in the F<.consign> file even though the contents
+have changed.
+
+=back
+
+The Cons default behavior of always calculating a source file's
+signature from the file's contents is equivalent to specifying:
+
+ SourceSignature '*' => 'content';
+
+The C<*> will match all source files. The C<content> keyword
+specifies that Cons will read the contents of a source file to calculate
+its signature each time it is run.
+
+A useful global performance optimization is:
+
+ SourceSignature '*' => 'stored-content';
+
+This specifies that Cons will use pre-computed content signatures
+from F<.consign> files, when available, rather than re-calculating a
+signature from the the source file's contents each time Cons is run. In
+practice, this is safe for most build situations, and only a problem
+when source files are changed automatically (by scripts, for example).
+The Cons default, however, errs on the side of guaranteeing a correct
+build in all situations.
+
+Cons tries to match source file path names against the patterns in the
+order they are specified in the C<SourceSignature> arguments:
+
+ SourceSignature '/usr/repository/objects/*' => 'stored-content',
+ '/usr/repository/*' => 'content',
+ '*.y' => 'content',
+ '*' => 'stored-content';
+
+In this example, all source files under the F</usr/repository/objects>
+directory will use F<.consign> file content signatures, source files
+anywhere else underneath F</usr/repository> will not use F<.consign>
+signature values, all Yacc source files (C<*.y>) anywhere else will not
+use F<.consign> signature values, and any other source file will use
+F<.consign> signature values.
+
+
+=head2 Derived-file signature configuration
+
+Cons provides a C<SIGNATURE> construction variable that allows you to
+configure how signatures are calculated for any derived file when its
+signature is being used to decide if a dependent file is up-to-date.
+The value of the C<SIGNATURE> construction variable is a Perl array
+reference that holds one or more pairs of strings, like the arguments to
+the C<SourceSignature> method.
+
+The first string in each pair is a pattern to match against derived file
+path names. The pattern is a file-globbing pattern, not a Perl regular
+expression; the pattern `*.obj' will match all (Win32) object files.
+The C<*> wildcard will match across directory separators; the pattern
+`foo/*.a' would match all (UNIX) library archives in any subdirectory
+underneath the foo subdirectory.
+
+The second string in each pair contains one of the following keywords
+to specify how signatures should be calculated for derived files that
+match the pattern. The available keywords are the same as for the
+C<SourceSignature> method, with an additional keyword:
+
+=over 4
+
+=item build
+
+Use the build signature of the derived file when calculating signatures
+of files that depend on it. This guarantees correct builds by forcing
+Cons to rebuild any and all files that depend on the derived file.
+
+=item content
+
+Use the content signature of the derived file when calculating signatures
+of files that depend on it. This guarantees correct calculation of the
+file's signature for all builds, by telling Cons to read the contents of
+a derived file to calculate its content signature each time it is run.
+
+=item stored-content
+
+Use the derived file's content signature as stored in the F<.consign>
+file, provided the file's timestamp matches the cached timestamp value
+in the F<.consign> file. This optimizes performance, with the slight
+risk of an incorrect build if a derived file's contents have been
+changed so quickly after a Cons build that the file's timestamp still
+matches the stored timestamp in the F<.consign> file.
+
+=back
+
+The Cons default behavior (as previously described) for using
+derived-file signatures is equivalent to:
+
+ $env = new cons(SIGNATURE => ['*' => 'build']);
+
+The C<*> will match all derived files. The C<build> keyword specifies
+that all derived files' build signatures will be used when calculating
+whether a dependent file is up-to-date.
+
+A useful alternative default C<SIGNATURE> configuration for many sites:
+
+ $env = new cons(SIGNATURE => ['*' => 'content']);
+
+In this configuration, derived files have their signatures calculated
+from the file contents. This adds slightly to Cons' workload, but has
+the useful effect of "stopping" further rebuilds if a derived file is
+rebuilt to exactly the same file contents as before, which usually
+outweighs the additional computation Cons must perform.
+
+For example, changing a comment in a C file and recompiling should
+generate the exact same object file (assuming the compiler doesn't
+insert a timestamp in the object file's header). In that case,
+specifying C<content> or C<stored-content> for the signature calculation
+will cause Cons to recognize that the object file did not actually
+change as a result of being rebuilt, and libraries or programs that
+include the object file will not be rebuilt. When C<build> is
+specified, however, Cons will only "know" that the object file was
+rebuilt, and proceed to rebuild any additional files that include the
+object file.
+
+Note that Cons tries to match derived file path names against the
+patterns in the order they are specified in the C<SIGNATURE> array
+reference:
+
+ $env = new cons(SIGNATURE => ['foo/*.o' => 'build',
+ '*.o' => 'content',
+ '*.a' => 'stored-content',
+ '*' => 'content']);
+
+In this example, all object files underneath the F<foo> subdirectory
+will use build signatures, all other object files (including object
+files underneath other subdirectories!) will use F<.consign> file
+content signatures, libraries will use F<.consign> file build
+signatures, and all other derived files will use content signatures.
+
+
+=head2 Debugging signature calculation
+
+Cons provides a C<-S> option that can be used to specify what internal
+Perl package Cons should use to calculate signatures. The default Cons
+behavior is equivalent to specifying C<-S md5> on the command line.
+
+The only other package (currently) available is an C<md5::debug>
+package that prints out detailed information about the MD5 signature
+calculations performed by Cons:
+
+ % cons -S md5::debug hello
+ sig::md5::srcsig(hello.c)
+ => |52d891204c62fe93ecb95281e1571938|
+ sig::md5::collect(52d891204c62fe93ecb95281e1571938)
+ => |fb0660af4002c40461a2f01fbb5ffd03|
+ sig::md5::collect(52d891204c62fe93ecb95281e1571938,
+ fb0660af4002c40461a2f01fbb5ffd03,
+ cc -c %< -o %>)
+ => |f7128da6c3fe3c377dc22ade70647b39|
+ sig::md5::current(||
+ eq |f7128da6c3fe3c377dc22ade70647b39|)
+ cc -c hello.c -o hello.o
+ sig::md5::collect()
+ => |d41d8cd98f00b204e9800998ecf8427e|
+ sig::md5::collect(f7128da6c3fe3c377dc22ade70647b39,
+ d41d8cd98f00b204e9800998ecf8427e,
+ cc -o %> %< )
+ => |a0bdce7fd09e0350e7efbbdb043a00b0|
+ sig::md5::current(||
+ eq |a0bdce7fd09e0350e7efbbdb043a00b0|)
+ cc -o hello, hello.o
+
+
+=head1 Code Repositories
+
+Many software development organizations will have one or more central
+repository directory trees containing the current source code for one or
+more projects, as well as the derived object files, libraries, and
+executables. In order to reduce unnecessary recompilation, it is useful to
+use files from the repository to build development software--assuming, of
+course, that no newer dependency file exists in the local build tree.
+
+
+=head2 Repository
+
+Cons provides a mechanism to specify a list of code repositories that will
+be searched, in-order, for source files and derived files not found in the
+local build directory tree.
+
+The following lines in a F<Construct> file will instruct Cons to look first
+under the F</usr/experiment/repository> directory and then under the
+F</usr/product/repository> directory:
+
+ Repository qw (
+ /usr/experiment/repository
+ /usr/product/repository
+ );
+
+The repository directories specified may contain source files, derived files
+(objects, libraries and executables), or both. If there is no local file
+(source or derived) under the directory in which Cons is executed, then the
+first copy of a same-named file found under a repository directory will be
+used to build any local derived files.
+
+Cons maintains one global list of repositories directories. Cons will
+eliminate the current directory, and any non-existent directories, from the
+list.
+
+
+=head2 Finding the Construct file in a Repository
+
+Cons will also search for F<Construct> and F<Conscript> files in the
+repository tree or trees. This leads to a chicken-and-egg situation,
+though: how do you look in a repository tree for a F<Construct> file if the
+F<Construct> file tells you where the repository is? To get around this,
+repositories may be specified via C<-R> options on the command line:
+
+ % cons -R /usr/experiment/repository -R /usr/product/repository .
+
+Any repository directories specified in the F<Construct> or F<Conscript>
+files will be appended to the repository directories specified by
+command-line C<-R> options.
+
+=head2 Repository source files
+
+If the source code (include the F<Conscript> file) for the library version
+of the I<Hello, World!> C application is in a repository (with no derived
+files), Cons will use the repository source files to create the local object
+files and executable file:
+
+ % cons -R /usr/src_only/repository hello
+ gcc -c /usr/src_only/repository/hello.c -o hello.o
+ gcc -c /usr/src_only/repository/world.c -o world.o
+ ar r libworld.a world.o
+ ar: creating libworld.a
+ ranlib libworld.a
+ gcc -o hello hello.o libworld.a
+
+Creating a local source file will cause Cons to rebuild the appropriate
+derived file or files:
+
+ % pico world.c
+ [EDIT]
+ % cons -R /usr/src_only/repository hello
+ gcc -c world.c -o world.o
+ ar r libworld.a world.o
+ ar: creating libworld.a
+ ranlib libworld.a
+ gcc -o hello hello.o libworld.a
+
+And removing the local source file will cause Cons to revert back to
+building the derived files from the repository source:
+
+ % rm world.c
+ % cons -R /usr/src_only/repository hello
+ gcc -c /usr/src_only/repository/world.c -o world.o
+ ar r libworld.a world.o
+ ar: creating libworld.a
+ ranlib libworld.a
+ gcc -o hello hello.o libworld.a
+
+
+=head2 Repository derived files
+
+If a repository tree contains derived files (usually object files,
+libraries, or executables), Cons will perform its normal signature
+calculation to decide whether the repository file is up-to-date or a derived
+file must be built locally. This means that, in order to ensure correct
+signature calculation, a repository tree must also contain the F<.consign>
+files that were created by Cons when generating the derived files.
+
+This would usually be accomplished by building the software in the
+repository (or, alternatively, in a build directory, and then copying the
+result to the repository):
+
+ % cd /usr/all/repository
+ % cons hello
+ gcc -c hello.c -o hello.o
+ gcc -c world.c -o world.o
+ ar r libworld.a world.o
+ ar: creating libworld.a
+ ranlib libworld.a
+ gcc -o hello hello.o libworld.a
+
+(This is safe even if the F<Construct> file lists the F</usr/all/repository>
+directory in a C<Repository> command because Cons will remove the current
+directory from the repository list.)
+
+Now if we want to build a copy of the application with our own F<hello.c>
+file, we only need to create the one necessary source file, and use the
+C<-R> option to have Cons use other files from the repository:
+
+ % mkdir $HOME/build1
+ % cd $HOME/build1
+ % ed hello.c
+ [EDIT]
+ % cons -R /usr/all/repository hello
+ gcc -c hello.c -o hello.o
+ gcc -o hello hello.o /usr/all/repository/libworld.a
+
+Notice that Cons has not bothered to recreate a local F<libworld.a> library
+(or recompile the F<world.o> module), but instead uses the already-compiled
+version from the repository.
+
+Because the MD5 signatures that Cons puts in the F<.consign> file contain
+timestamps for the derived files, the signature timestamps must match the
+file timestamps for a signature to be considered valid.
+
+Some software systems may alter the timestamps on repository files (by
+copying them, e.g.), in which case Cons will, by default, assume the
+repository signatures are invalid and rebuild files unnecessarily. This
+behavior may be altered by specifying:
+
+ Repository_Sig_Times_OK 0;
+
+This tells Cons to ignore timestamps when deciding whether a signature is
+valid. (Note that avoiding this sanity check means there must be proper
+control over the repository tree to ensure that the derived files cannot be
+modified without updating the F<.consign> signature.)
+
+
+=head2 Local copies of files
+
+If the repository tree contains the complete results of a build, and we try
+to build from the repository without any files in our local tree, something
+moderately surprising happens:
+
+ % mkdir $HOME/build2
+ % cd $HOME/build2
+ % cons -R /usr/all/repository hello
+ cons: "hello" is up-to-date.
+
+Why does Cons say that the F<hello> program is up-to-date when there is no
+F<hello> program in the local build directory? Because the repository (not
+the local directory) contains the up-to-date F<hello> program, and Cons
+correctly determines that nothing needs to be done to rebuild this
+up-to-date copy of the file.
+
+There are, however, many times in which it is appropriate to ensure that a
+local copy of a file always exists. A packaging or testing script, for
+example, may assume that certain generated files exist locally. Instead of
+making these subsidiary scripts aware of the repository directory, the
+C<Local> command may be added to a F<Construct> or F<Conscript> file to
+specify that a certain file or files must appear in the local build
+directory:
+
+ Local qw(
+ hello
+ );
+
+Then, if we re-run the same command, Cons will make a local copy of the
+program from the repository copy (telling you that it is doing so):
+
+ % cons -R /usr/all/repository hello
+ Local copy of hello from /usr/all/repository/hello
+ cons: "hello" is up-to-date.
+
+Notice that, because the act of making the local copy is not considered a
+"build" of the F<hello> file, Cons still reports that it is up-to-date.
+
+Creating local copies is most useful for files that are being installed into
+an intermediate directory (for sharing with other directories) via the
+C<Install> command. Accompanying the C<Install> command for a file with a
+companion C<Local> command is so common that Cons provides a
+C<Install_Local> command as a convenient way to do both:
+
+ Install_Local $env, '#export', 'hello';
+
+is exactly equivalent to:
+
+ Install $env '#export', 'hello';
+ Local '#export/hello';
+
+Both the C<Local> and C<Install_Local> commands update the local F<.consign>
+file with the appropriate file signatures, so that future builds are
+performed correctly.
+
+
+=head2 Repository dependency analysis
+
+Due to its built-in scanning, Cons will search the specified repository
+trees for included F<.h> files. Unless the compiler also knows about the
+repository trees, though, it will be unable to find F<.h> files that only
+exist in a repository. If, for example, the F<hello.c> file includes the
+F<hello.h> file in its current directory:
+
+ % cons -R /usr/all/repository hello
+ gcc -c /usr/all/repository/hello.c -o hello.o
+ /usr/all/repository/hello.c:1: hello.h: No such file or directory
+
+Solving this problem forces some requirements onto the way construction
+environments are defined and onto the way the C C<#include> preprocessor
+directive is used to include files.
+
+In order to inform the compiler about the repository trees, Cons will add
+appropriate C<-I> flags to the compilation commands. This means that the
+C<CPPPATH> variable in the construction environment must explicitly specify
+all subdirectories which are to be searched for included files, including the
+current directory. Consequently, we can fix the above example by changing
+the environment creation in the F<Construct> file as follows:
+
+ $env = new cons(
+ CC => 'gcc',
+ CPPPATH => '.',
+ LIBS => 'libworld.a',
+ );
+
+Due to the definition of the C<CPPPATH> variable, this yields, when we
+re-execute the command:
+
+ % cons -R /usr/all/repository hello
+ gcc -c -I. -I/usr/all/repository /usr/all/repository/hello.c -o hello.o
+ gcc -o hello hello.o /usr/all/repository/libworld.a
+
+The order of the C<-I> flags replicates, for the C preprocessor, the same
+repository-directory search path that Cons uses for its own dependency
+analysis. If there are multiple repositories and multiple C<CPPPATH>
+directories, Cons will append the repository directories to the beginning of
+each C<CPPPATH> directory, rapidly multiplying the number of C<-I> flags.
+As an extreme example, a F<Construct> file containing:
+
+ Repository qw(
+ /u1
+ /u2
+ );
+
+ $env = new cons(
+ CPPPATH => 'a:b:c',
+ );
+
+Would yield a compilation command of:
+
+ cc -Ia -I/u1/a -I/u2/a -Ib -I/u1/b -I/u2/b -Ic -I/u1/c -I/u2/c -c hello.c -o hello.o
+
+In order to shorten the command lines as much as possible, Cons will
+remove C<-I> flags for any directories, locally or in the repositories,
+which do not actually exist. (Note that the C<-I> flags are not included
+in the MD5 signature calculation for the target file, so the target will
+not be recompiled if the compilation command changes due to a directory
+coming into existence.)
+
+Because Cons relies on the compiler's C<-I> flags to communicate the
+order in which repository directories must be searched, Cons' handling
+of repository directories is fundamentally incompatible with using
+double-quotes on the C<#include> directives in any C source code that
+you plan to modify:
+
+ #include "file.h" /* DON'T USE DOUBLE-QUOTES LIKE THIS */
+
+This is because most C preprocessors, when faced with such a directive, will
+always first search the directory containing the source file. This
+undermines the elaborate C<-I> options that Cons constructs to make the
+preprocessor conform to its preferred search path.
+
+Consequently, when using repository trees in Cons, B<always> use
+angle-brackets for included files in any C source (.c or .h) files that
+you plan to modify locally:
+
+ #include <file.h> /* USE ANGLE-BRACKETS INSTEAD */
+
+Code that will not change can still safely use double quotes on #include
+lines.
+
+
+=head2 Repository_List
+
+Cons provides a C<Repository_List> command to return a list of all
+repository directories in their current search order. This can be used for
+debugging, or to do more complex Perl stuff:
+
+ @list = Repository_List;
+ print join(' ', @list), "\n";
+
+
+=head2 Repository interaction with other Cons features
+
+Cons' handling of repository trees interacts correctly with other Cons
+features--which is to say, it generally does what you would expect.
+
+Most notably, repository trees interact correctly, and rather powerfully,
+with the 'Link' command. A repository tree may contain one or more
+subdirectories for version builds established via C<Link> to a source
+subdirectory. Cons will search for derived files in the appropriate build
+subdirectories under the repository tree.
+
+
+=head1 Default targets
+
+Until now, we've demonstrated invoking Cons with an explicit target
+to build:
+
+ % cons hello
+
+Normally, Cons does not build anything unless a target is specified,
+but specifying '.' (the current directory) will build everything:
+
+ % cons # does not build anything
+
+ % cons . # builds everything under the top-level directory
+
+Adding the C<Default> method to any F<Construct> or F<Conscript> file will add
+the specified targets to a list of default targets. Cons will build
+these defaults if there are no targets specified on the command line.
+So adding the following line to the top-level F<Construct> file will mimic
+Make's typical behavior of building everything by default:
+
+ Default '.';
+
+The following would add the F<hello> and F<goodbye> commands (in the
+same directory as the F<Construct> or F<Conscript> file) to the default list:
+
+ Default qw(
+ hello
+ goodbye
+ );
+
+The C<Default> method may be used more than once to add targets to the
+default list.
+
+=head1 Selective builds
+
+Cons provides two methods for reducing the size of given build. The first is
+by specifying targets on the command line, and the second is a method for
+pruning the build tree. We'll consider target specification first.
+
+
+=head2 Selective targeting
+
+Like make, Cons allows the specification of ``targets'' on the command
+line. Cons targets may be either files or directories. When a directory is
+specified, this is simply a short-hand notation for every derivable
+product--that Cons knows about--in the specified directory and below. For
+example:
+
+ % cons build/hello/hello.o
+
+means build F<hello.o> and everything that F<hello.o> might need. This is
+from a previous version of the B<Hello, World!> program in which F<hello.o>
+depended upon F<export/include/world.h>. If that file is not up-to-date
+(because someone modified F<src/world/world.h)>, then it will be rebuilt,
+even though it is in a directory remote from F<build/hello>.
+
+In this example:
+
+ % cons build
+
+Everything in the F<build> directory is built, if necessary. Again, this may
+cause more files to be built. In particular, both F<export/include/world.h>
+and F<export/lib/libworld.a> are required by the F<build/hello> directory,
+and so they will be built if they are out-of-date.
+
+If we do, instead:
+
+ % cons export
+
+then only the files that should be installed in the export directory will be
+rebuilt, if necessary, and then installed there. Note that C<cons build>
+might build files that C<cons export> doesn't build, and vice-versa.
+
+
+=head2 No ``special'' targets
+
+With Cons, make-style ``special'' targets are not required. The simplest
+analog with Cons is to use special F<export> directories, instead. Let's
+suppose, for example, that you have a whole series of unit tests that are
+associated with your code. The tests live in the source directory near the
+code. Normally, however, you don't want to build these tests. One solution
+is to provide all the build instructions for creating the tests, and then to
+install the tests into a separate part of the tree. If we install the tests
+in a top-level directory called F<tests>, then:
+
+ % cons tests
+
+will build all the tests.
+
+ % cons export
+
+will build the production version of the system (but not the tests), and:
+
+ % cons build
+
+should probably be avoided (since it will compile tests unnecessarily).
+
+If you want to build just a single test, then you could explicitly name the
+test (in either the F<tests> directory or the F<build> directory). You could
+also aggregate the tests into a convenient hierarchy within the tests
+directory. This hierarchy need not necessarily match the source hierarchy,
+in much the same manner that the include hierarchy probably doesn't match
+the source hierarchy (the include hierarchy is unlikely to be more than two
+levels deep, for C programs).
+
+If you want to build absolutely everything in the tree (subject to whatever
+options you select), you can use:
+
+ % cons .
+
+This is not particularly efficient, since it will redundantly walk all the
+trees, including the source tree. The source tree, of course, may have
+buildable objects in it--nothing stops you from doing this, even if you
+normally build in a separate build tree.
+
+
+=head1 Build Pruning
+
+In conjunction with target selection, B<build pruning> can be used to reduce
+the scope of the build. In the previous peAcH and baNaNa example, we have
+already seen how script-driven build pruning can be used to make only half
+of the potential build available for any given invocation of C<cons>. Cons
+also provides, as a convenience, a command line convention that allows you
+to specify which F<Conscript> files actually get ``built''--that is,
+incorporated into the build tree. For example:
+
+ % cons build +world
+
+The C<+> argument introduces a Perl regular expression. This must, of
+course, be quoted at the shell level if there are any shell meta-characters
+within the expression. The expression is matched against each F<Conscript>
+file which has been mentioned in a C<Build> statement, and only those
+scripts with matching names are actually incorporated into the build
+tree. Multiple such arguments are allowed, in which case a match against any
+of them is sufficient to cause a script to be included.
+
+In the example, above, the F<hello> program will not be built, since Cons
+will have no knowledge of the script F<hello/Conscript>. The F<libworld.a>
+archive will be built, however, if need be.
+
+There are a couple of uses for build pruning via the command line. Perhaps
+the most useful is the ability to make local changes, and then, with
+sufficient knowledge of the consequences of those changes, restrict the size
+of the build tree in order to speed up the rebuild time. A second use for
+build pruning is to actively prevent the recompilation of certain files that
+you know will recompile due to, for example, a modified header file. You may
+know that either the changes to the header file are immaterial, or that the
+changes may be safely ignored for most of the tree, for testing
+purposes.With Cons, the view is that it is pragmatic to admit this type of
+behavior, with the understanding that on the next full build everything that
+needs to be rebuilt will be. There is no equivalent to a ``make touch''
+command, to mark files as permanently up-to-date. So any risk that is
+incurred by build pruning is mitigated. For release quality work, obviously,
+we recommend that you do not use build pruning (it's perfectly OK to use
+during integration, however, for checking compilation, etc. Just be sure to
+do an unconstrained build before committing the integration).
+
+
+=head1 Temporary overrides
+
+Cons provides a very simple mechanism for overriding aspects of a build. The
+essence is that you write an override file containing one or more
+C<Override> commands, and you specify this on the command line, when you run
+C<cons>:
+
+ % cons -o over export
+
+will build the F<export> directory, with all derived files subject to the
+overrides present in the F<over> file. If you leave out the C<-o> option,
+then everything necessary to remove all overrides will be rebuilt.
+
+
+=head2 Overriding environment variables
+
+The override file can contain two types of overrides. The first is incoming
+environment variables. These are normally accessible by the F<Construct>
+file from the C<%ENV> hash variable. These can trivially be overridden in
+the override file by setting the appropriate elements of C<%ENV> (these
+could also be overridden in the user's environment, of course).
+
+
+=head2 The Override command
+
+The second type of override is accomplished with the C<Override> command,
+which looks like this:
+
+ Override <regexp>, <var1> => <value1>, <var2> => <value2>, ...;
+
+The regular expression I<regexp> is matched against every derived file that
+is a candidate for the build. If the derived file matches, then the
+variable/value pairs are used to override the values in the construction
+environment associated with the derived file.
+
+Let's suppose that we have a construction environment like this:
+
+ $CONS = new cons(
+ COPT => '',
+ CDBG => '-g',
+ CFLAGS => '%COPT %CDBG',
+ );
+
+Then if we have an override file F<over> containing this command:
+
+ Override '\.o$', COPT => '-O', CDBG => '';
+
+then any C<cons> invocation with C<-o over> that creates F<.o> files via
+this environment will cause them to be compiled with C<-O >and no C<-g>. The
+override could, of course, be restricted to a single directory by the
+appropriate selection of a regular expression.
+
+Here's the original version of the Hello, World! program, built with this
+environment. Note that Cons rebuilds the appropriate pieces when the
+override is applied or removed:
+
+ % cons hello
+ cc -g -c hello.c -o hello.o
+ cc -o hello hello.o
+ % cons -o over hello
+ cc -O -c hello.c -o hello.o
+ cc -o hello hello.o
+ % cons -o over hello
+ cons: "hello" is up-to-date.
+ % cons hello
+ cc -g -c hello.c -o hello.o
+ cc -o hello hello.o
+
+It's important that the C<Override> command only be used for temporary,
+on-the-fly overrides necessary for development because the overrides are not
+platform independent and because they rely too much on intimate knowledge of
+the workings of the scripts. For temporary use, however, they are exactly
+what you want.
+
+Note that it is still useful to provide, say, the ability to create a fully
+optimized version of a system for production use--from the F<Construct> and
+F<Conscript> files. This way you can tailor the optimized system to the
+platform. Where optimizer trade-offs need to be made (particular files may
+not be compiled with full optimization, for example), then these can be
+recorded for posterity (and reproducibility) directly in the scripts.
+
+
+=head1 More on construction environments
+
+As previously mentioned, a B<construction environment> is an object that
+has a set of keyword/value pairs and a set of methods, and which is used
+to tell Cons how target files should be built. This section describes
+how Cons uses and expands construction environment values to control its
+build behavior.
+
+=head2 Construction variable expansion
+
+Construction variables from a construction environment are expanded
+by preceding the keyword with a C<%> (percent sign):
+
+ Construction variables:
+ XYZZY => 'abracadabra',
+
+ The string: "The magic word is: %XYZZY!"
+ expands to: "The magic word is: abracadabra!"
+
+A construction variable name may be surrounded by C<{> and C<}> (curly
+braces), which are stripped as part of the expansion. This can
+sometimes be necessary to separate a variable expansion from trailing
+alphanumeric characters:
+
+ Construction variables:
+ OPT => 'value1',
+ OPTION => 'value2',
+
+ The string: "%OPT %{OPT}ION %OPTION %{OPTION}"
+ expands to: "value1 value1ION value2 value2"
+
+Construction variable expansion is recursive--that is, a string
+containing C<%->expansions after substitution will be re-expanded until
+no further substitutions can be made:
+
+ Construction variables:
+ STRING => 'The result is: %FOO',
+ FOO => '%BAR',
+ BAR => 'final value',
+
+ The string: "The string says: %STRING"
+ expands to: "The string says: The result is: final value"
+
+If a construction variable is not defined in an environment, then the
+null string is substituted:
+
+ Construction variables:
+ FOO => 'value1',
+ BAR => 'value2',
+
+ The string: "%FOO <%NO_VARIABLE> %BAR"
+ expands to: "value1 <> value2"
+
+A doubled C<%%> will be replaced by a single C<%>:
+
+ The string: "Here is a percent sign: %%"
+ expands to: "Here is a percent sign: %"
+
+=head2 Default construction variables
+
+When you specify no arguments when creating a new construction
+environment:
+
+ $env = new cons();
+
+Cons creates a reference to a new, default construction
+environment. This contains a number of construction variables and some
+methods. At the present writing, the default construction variables on a
+UNIX system are:
+
+ CC => 'cc',
+ CFLAGS => '',
+ CCCOM => '%CC %CFLAGS %_IFLAGS -c %< -o %>',
+ CXX => '%CC',
+ CXXFLAGS => '%CFLAGS',
+ CXXCOM => '%CXX %CXXFLAGS %_IFLAGS -c %< -o %>',
+ INCDIRPREFIX => '-I',
+ INCDIRSUFFIX => '',
+ LINK => '%CXX',
+ LINKCOM => '%LINK %LDFLAGS -o %> %< %_LDIRS %LIBS',
+ LINKMODULECOM => '%LD -r -o %> %<',
+ LIBDIRPREFIX => '-L',
+ LIBDIRSUFFIX => '',
+ AR => 'ar',
+ ARFLAGS => 'r',
+ ARCOM => ['%AR %ARFLAGS %> %<', '%RANLIB %>'],
+ RANLIB => 'ranlib',
+ AS => 'as',
+ ASFLAGS => '',
+ ASCOM => '%AS %ASFLAGS %< -o %>',
+ LD => 'ld',
+ LDFLAGS => '',
+ PREFLIB => 'lib',
+ SUFLIB => '.a',
+ SUFLIBS => '.so:.a',
+ SUFOBJ => '.o',
+ SIGNATURE => [ '*' => 'build' ],
+ ENV => { 'PATH' => '/bin:/usr/bin' },
+
+
+And on a Win32 system (Windows NT), the default construction variables
+are (unless the default rule style is set using the B<DefaultRules>
+method):
+
+ CC => 'cl',
+ CFLAGS => '/nologo',
+ CCCOM => '%CC %CFLAGS %_IFLAGS /c %< /Fo%>',
+ CXXCOM => '%CXX %CXXFLAGS %_IFLAGS /c %< /Fo%>',
+ INCDIRPREFIX => '/I',
+ INCDIRSUFFIX => '',
+ LINK => 'link',
+ LINKCOM => '%LINK %LDFLAGS /out:%> %< %_LDIRS %LIBS',
+ LINKMODULECOM => '%LD /r /o %> %<',
+ LIBDIRPREFIX => '/LIBPATH:',
+ LIBDIRSUFFIX => '',
+ AR => 'lib',
+ ARFLAGS => '/nologo ',
+ ARCOM => "%AR %ARFLAGS /out:%> %<",
+ RANLIB => '',
+ LD => 'link',
+ LDFLAGS => '/nologo ',
+ PREFLIB => '',
+ SUFEXE => '.exe',
+ SUFLIB => '.lib',
+ SUFLIBS => '.dll:.lib',
+ SUFOBJ => '.obj',
+ SIGNATURE => [ '*' => 'build' ],
+
+These variables are used by the various methods associated with the
+environment. In particular, any method that ultimately invokes an external
+command will substitute these variables into the final command, as
+appropriate. For example, the C<Objects> method takes a number of source
+files and arranges to derive, if necessary, the corresponding object
+files:
+
+ Objects $env 'foo.c', 'bar.c';
+
+This will arrange to produce, if necessary, F<foo.o> and F<bar.o>. The
+command invoked is simply C<%CCCOM>, which expands, through substitution,
+to the appropriate external command required to build each object. The
+substitution rules will be discussed in detail in the next section.
+
+The construction variables are also used for other purposes. For example,
+C<CPPPATH> is used to specify a colon-separated path of include
+directories. These are intended to be passed to the C preprocessor and are
+also used by the C-file scanning machinery to determine the dependencies
+involved in a C Compilation.
+
+Variables beginning with underscore are created by various methods,
+and should normally be considered ``internal'' variables. For example,
+when a method is called which calls for the creation of an object from
+a C source, the variable C<_IFLAGS> is created: this corresponds to the
+C<-I> switches required by the C compiler to represent the directories
+specified by C<CPPPATH>.
+
+Note that, for any particular environment, the value of a variable is set
+once, and then never reset (to change a variable, you must create a new
+environment. Methods are provided for copying existing environments for this
+purpose). Some internal variables, such as C<_IFLAGS> are created on demand,
+but once set, they remain fixed for the life of the environment.
+
+The C<CFLAGS>, C<LDFLAGS>, and C<ARFLAGS> variables all supply a place
+for passing options to the compiler, loader, and archiver, respectively.
+
+The C<INCDIRPREFIX> and C<INCDIRSUFFIX> variables specify option
+strings to be appended to the beginning and end, respectively, of each
+include directory so that the compiler knows where to find F<.h> files.
+Similarly, the C<LIBDIRPREFIX> and C<LIBDIRSUFFIX> variables specify the
+option string to be appended to the beginning of and end, respectively,
+of each directory that the linker should search for libraries.
+
+Another variable, C<ENV>, is used to determine the system environment during
+the execution of an external command. By default, the only environment
+variable that is set is C<PATH>, which is the execution path for a UNIX
+command. For the utmost reproducibility, you should really arrange to set
+your own execution path, in your top-level F<Construct> file (or perhaps by
+importing an appropriate construction package with the Perl C<use>
+command). The default variables are intended to get you off the ground.
+
+=head2 Expanding variables in construction commands
+
+Within a construction command, construction variables will be expanded
+according to the rules described above. In addition to normal variable
+expansion from the construction environment, construction commands also
+expand the following pseudo-variables to insert the specific input and
+output files in the command line that will be executed:
+
+=over 10
+
+=item %>
+
+The target file name. In a multi-target command, this expands to the
+first target mentioned.)
+
+=item %0
+
+Same as C<%E<gt>>.
+
+=item %1, %2, ..., %9
+
+These refer to the first through ninth input file, respectively.
+
+=item %E<lt>
+
+The full set of input file names. If any of these have been used
+anywhere else in the current command line (via C<%1>, C<%2>, etc.), then
+those will be deleted from the list provided by C<%E<lt>>. Consider the
+following command found in a F<Conscript> file in the F<test> directory:
+
+ Command $env 'tgt', qw(foo bar baz), qq(
+ echo %< -i %1 > %>
+ echo %< -i %2 >> %>
+ echo %< -i %3 >> %>
+ );
+
+If F<tgt> needed to be updated, then this would result in the execution of
+the following commands, assuming that no remapping has been established for
+the F<test> directory:
+
+ echo test/bar test/baz -i test/foo > test/tgt
+ echo test/foo test/baz -i test/bar >> test/tgt
+ echo test/foo test/bar -i test/baz >> test/tgt
+
+=back
+
+Any of the above pseudo-variables may be followed immediately by one of
+the following suffixes to select a portion of the expanded path name:
+
+ :a the absolute path to the file name
+ :b the directory plus the file name stripped of any suffix
+ :d the directory
+ :f the file name
+ :s the file name suffix
+ :F the file name stripped of any suffix
+ :S the absolute path path to a Linked source file
+
+Continuing with the above example, C<%E<lt>:f> would expand to C<foo bar baz>,
+and C<%E<gt>:d> would expand to C<test>.
+
+There are additional C<%> elements which affect the command line(s):
+
+=over 10
+
+=item %[ %]
+
+It is possible to programmatically rewrite part of the command by
+enclosing part of it between C<%[> and C<%]>. This will call the
+construction variable named as the first word enclosed in the brackets
+as a Perl code reference; the results of this call will be used to
+replace the contents of the brackets in the command line. For example,
+given an existing input file named F<tgt.in>:
+
+ @keywords = qw(foo bar baz);
+ $env = new cons(X_COMMA => sub { join(",", @_) });
+ Command $env 'tgt', 'tgt.in', qq(
+ echo '# Keywords: %[X_COMMA @keywords %]' > %>
+ cat %< >> %>
+ );
+
+This will execute:
+
+ echo '# Keywords: foo,bar,baz' > tgt
+ cat tgt.in >> tgt
+
+=item %( %)
+
+Cons includes the text of the command line in the MD5 signature for a
+build, so that targets get rebuilt if you change the command line (to
+add or remove an option, for example). Command-line text in between
+C<%(> and C<%)>, however, will be ignored for MD5 signature calculation.
+
+Internally, Cons uses C<%(> and C<%)> around include and library
+directory options (C<-I> and C<-L> on UNIX systems, C</I> and
+C</LIBPATH> on Windows NT) to avoid rebuilds just because the directory
+list changes. Rebuilds occur only if the changed directory list causes
+any included I<files> to change, and a changed include file is detected
+by the MD5 signature calculation on the actual file contents.
+
+=back
+
+=head2 Expanding construction variables in file names
+
+Cons expands construction variables in the source and target file names
+passed to the various construction methods according to the expansion
+rules described above:
+
+ $env = new cons(
+ DESTDIR => 'programs',
+ SRCDIR => 'src',
+ );
+ Program $env '%DESTDIR/hello', '%SRCDIR/hello.c';
+
+This allows for flexible configuration, through the construction
+environment, of directory names, suffixes, etc.
+
+
+=head1 Build actions
+
+Cons supports several types of B<build actions> that can be performed
+to construct one or more target files. Usually, a build action is
+a construction command--that is, a command-line string that invokes
+an external command. Cons can also execute Perl code embedded in a
+command-line string, and even supports an experimental ability to build
+a target file by executing a Perl code reference directly.
+
+A build action is usually specified as the value of a construction
+variable:
+
+ $env = new cons(
+ CCCOM => '%CC %CFLAGS %_IFLAGS -c %< -o %>',
+ LINKCOM => '[perl] &link_executable("%>", "%<")',
+ ARCOM => sub { my($env, $target, @sources) = @_;
+ # code to create an archive
+ }
+ );
+
+A build action may be associated directly with one or more target files
+via the C<Command> method; see below.
+
+=head2 Construction commands
+
+A construction command goes through expansion of construction variables
+and C<%-> pseudo-variables, as described above, to create the actual
+command line that Cons will execute to generate the target file or
+files.
+
+After substitution occurs, strings of white space are converted into
+single blanks, and leading and trailing white space is eliminated. It
+is therefore currently not possible to introduce variable length white
+space in strings passed into a command.
+
+If a multi-line command string is provided, the commands are executed
+sequentially. If any of the commands fails, then none of the rest are
+executed, and the target is not marked as updated, i.e. a new signature is
+not stored for the target.
+
+Normally, if all the commands succeed, and return a zero status (or whatever
+platform-specific indication of success is required), then a new signature
+is stored for the target. If a command erroneously reports success even
+after a failure, then Cons will assume that the target file created by that
+command is accurate and up-to-date.
+
+The first word of each command string, after expansion, is assumed to be an
+executable command looked up on the C<PATH> environment variable (which is,
+in turn, specified by the C<ENV> construction variable). If this command is
+found on the path, then the target will depend upon it: the command will
+therefore be automatically built, as necessary. It's possible to write
+multi-part commands to some shells, separated by semi-colons. Only the first
+command word will be depended upon, however, so if you write your command
+strings this way, you must either explicitly set up a dependency (with the
+C<Depends> method), or be sure that the command you are using is a system
+command which is expected to be available. If it isn't available, you will,
+of course, get an error.
+
+Cons normally prints a command before executing it. This behavior is
+suppressed if the first character of the command is C<@>. Note that
+you may need to separate the C<@> from the command name or escape it to
+prevent C<@cmd> from looking like an array to Perl quote operators that
+perform interpolation:
+
+ # The first command line is incorrect,
+ # because "@cp" looks like an array
+ # to the Perl qq// function.
+ # Use the second form instead.
+ Command $env 'foo', 'foo.in', qq(
+ @cp %< tempfile
+ @ cp tempfile %>
+ );
+
+If there are shell meta characters anywhere in the expanded command line,
+such as C<E<lt>>, C<E<gt>>, quotes, or semi-colon, then the command
+will actually be executed by invoking a shell. This means that a command
+such as:
+
+ cd foo
+
+alone will typically fail, since there is no command C<cd> on the path. But
+the command string:
+
+ cd $<:d; tar cf $>:f $<:f
+
+when expanded will still contain the shell meta character semi-colon, and a
+shell will be invoked to interpret the command. Since C<cd> is interpreted
+by this sub-shell, the command will execute as expected.
+
+=head2 Perl expressions
+
+If any command (even one within a multi-line command) begins with
+C<[perl]>, the remainder of that command line will be evaluated by the
+running Perl instead of being forked by the shell. If an error occurs
+in parsing the Perl code, or if the Perl expression returns 0 or undef,
+the command will be considered to have failed. For example, here is a
+simple command which creates a file C<foo> directly from Perl:
+
+ $env = new cons();
+ Command $env 'foo',
+ qq([perl] open(FOO,'>foo');print FOO "hi\\n"; close(FOO); 1);
+
+Note that when the command is executed, you are in the same package as
+when the F<Construct> or F<Conscript> file was read, so you can call
+Perl functions you've defined in the same F<Construct> or F<Conscript>
+file in which the C<Command> appears:
+
+ $env = new cons();
+ sub create_file {
+ my $file = shift;
+ open(FILE, ">$file");
+ print FILE "hi\n";
+ close(FILE);
+ return 1;
+ }
+ Command $env 'foo', "[perl] &create_file('%>')";
+
+The Perl string will be used to generate the signature for the derived
+file, so if you change the string, the file will be rebuilt. The contents
+of any subroutines you call, however, are not part of the signature,
+so if you modify a called subroutine such as C<create_file> above,
+the target will I<not> be rebuilt. Caveat user.
+
+=head2 Perl code references [EXPERIMENTAL]
+
+Cons supports the ability to create a derived file by directly executing
+a Perl code reference. This feature is considered EXPERIMENTAL and
+subject to change in the future.
+
+A code reference may either be a named subroutine referenced by the
+usual C<\&> syntax:
+
+ sub build_output {
+ my($env, $target, @sources) = @_;
+ print "build_output building $target\n";
+ open(OUT, ">$target");
+ foreach $src (@sources) {
+ if (! open(IN, "<$src")) {
+ print STDERR "cannot open '$src': $!\n";
+ return undef;
+ }
+ print OUT, <IN>;
+ }
+ close(OUT);
+ return 1;
+ }
+ Command $env 'output', \&build_output;
+
+or the code reference may be an anonymous subroutine:
+
+ Command $env 'output', sub {
+ my($env, $target, @sources) = @_;
+ print "building $target\n";
+ open(FILE, ">$target");
+ print FILE "hello\n";
+ close(FILE);
+ return 1;
+ };
+
+To build the target file, the referenced subroutine is passed, in order:
+the construction environment used to generate the target; the path
+name of the target itself; and the path names of all the source files
+necessary to build the target file.
+
+The code reference is expected to generate the target file, of course,
+but may manipulate the source and target files in any way it chooses.
+The code reference must return a false value (C<undef> or C<0>) if
+the build of the file failed. Any true value indicates a successful
+build of the target.
+
+Building target files using code references is considered EXPERIMENTAL
+due to the following current limitations:
+
+=over 4
+
+Cons does I<not> print anything to indicate the code reference is being
+called to build the file. The only way to give the user any indication
+is to have the code reference explicitly print some sort of "building"
+message, as in the above examples.
+
+Cons does not generate any signatures for code references, so if the
+code in the reference changes, the target will I<not> be rebuilt.
+
+Cons has no public method to allow a code reference to extract
+construction variables. This would be good to allow generalization of
+code references based on the current construction environment, but would
+also complicate the problem of generating meaningful signatures for code
+references.
+
+=back
+
+Support for building targets via code references has been released in
+this version to encourage experimentation and the seeking of possible
+solutions to the above limitations.
+
+
+=head1 Default construction methods
+
+The list of default construction methods includes the following:
+
+
+=head2 The C<new> constructor
+
+The C<new> method is a Perl object constructor. That is, it is not invoked
+via a reference to an existing construction environment B<reference>, but,
+rather statically, using the name of the Perl B<package> where the
+constructor is defined. The method is invoked like this:
+
+ $env = new cons(<overrides>);
+
+The environment you get back is blessed into the package C<cons>, which
+means that it will have associated with it the default methods described
+below. Individual construction variables can be overridden by providing
+name/value pairs in an override list. Note that to override any command
+environment variable (i.e. anything under C<ENV>), you will have to override
+all of them. You can get around this difficulty by using the C<copy> method
+on an existing construction environment.
+
+
+=head2 The C<clone> method
+
+The C<clone> method creates a clone of an existing construction environment,
+and can be called as in the following example:
+
+ $env2 = $env1->clone(<overrides>);
+
+You can provide overrides in the usual manner to create a different
+environment from the original. If you just want a new name for the same
+environment (which may be helpful when exporting environments to existing
+components), you can just use simple assignment.
+
+
+=head2 The C<copy> method
+
+The C<copy> method extracts the externally defined construction variables
+from an environment and returns them as a list of name/value
+pairs. Overrides can also be provided, in which case, the overridden values
+will be returned, as appropriate. The returned list can be assigned to a
+hash, as shown in the prototype, below, but it can also be manipulated in
+other ways:
+
+ %env = $env1->copy(<overrides>);
+
+The value of C<ENV>, which is itself a hash, is also copied to a new hash,
+so this may be changed without fear of affecting the original
+environment. So, for example, if you really want to override just the
+C<PATH> variable in the default environment, you could do the following:
+
+ %cons = new cons()->copy();
+ $cons{ENV}{PATH} = "<your path here>";
+ $cons = new cons(%cons);
+
+This will leave anything else that might be in the default execution
+environment undisturbed.
+
+
+=head2 The C<Install> method
+
+The C<Install> method arranges for the specified files to be installed in
+the specified directory. The installation is optimized: the file is not
+copied if it can be linked. If this is not the desired behavior, you will
+need to use a different method to install the file. It is called as follows:
+
+ Install $env <directory>, <names>;
+
+Note that, while the files to be installed may be arbitrarily named,
+only the last component of each name is used for the installed target
+name. So, for example, if you arrange to install F<foo/bar> in F<baz>,
+this will create a F<bar> file in the F<baz> directory (not F<foo/bar>).
+
+
+=head2 The C<InstallAs> method
+
+The C<InstallAs> method arranges for the specified source file(s) to be
+installed as the specified target file(s). Multiple files should be
+specified as a file list. The installation is optimized: the file is not
+copied if it can be linked. If this is not the desired behavior, you will
+need to use a different method to install the file. It is called as follows:
+
+C<InstallAs> works in two ways:
+
+Single file install:
+
+ InstallAs $env TgtFile, SrcFile;
+
+Multiple file install:
+
+ InstallAs $env ['tgt1', 'tgt2'], ['src1', 'src2'];
+
+Or, even as:
+
+ @srcs = qw(src1 src2 src3);
+ @tgts = qw(tgt1 tgt2 tgt3);
+ InstallAs $env [@tgts], [@srcs];
+
+Both the target and the sources lists should be of the same length.
+
+=head2 The C<Precious> method
+
+The C<Precious> method asks cons not to delete the specified file or
+list of files before building them again. It is invoked as:
+
+ Precious <files>;
+
+This is especially useful for allowing incremental updates to libraries
+or debug information files which are updated rather than rebuilt anew each
+time. Cons will still delete the files when the C<-r> flag is specified.
+
+=head2 The C<AfterBuild> method
+
+The C<AfterBuild> method evaluates the specified perl string after
+building the given file or files (or finding that they are up to date).
+The eval will happen once per specified file. C<AfterBuild> is called
+as follows:
+
+ AfterBuild $env 'foo.o', qq(print "foo.o is up to date!\n");
+
+The perl string is evaluated in the C<script> package, and has access
+to all variables and subroutines defined in the F<Conscript> file in
+which the C<AfterBuild> method is called.
+
+=head2 The C<Command> method
+
+The C<Command> method is a catchall method which can be used to arrange for
+any build action to be executed to update the target. For this command, a
+target file and list of inputs is provided. In addition, a build action
+is specified as the last argument. The build action is typically a
+command line or lines, but may also contain Perl code to be executed;
+see the section above on build actions for details.
+
+The C<Command> method is called as follows:
+
+ Command $env <target>, <inputs>, <build action>;
+
+The target is made dependent upon the list of input files specified, and the
+inputs must be built successfully or Cons will not attempt to build the
+target.
+
+To specify a command with multiple targets, you can specify a reference to a
+list of targets. In Perl, a list reference can be created by enclosing a
+list in square brackets. Hence the following command:
+
+ Command $env ['foo.h', 'foo.c'], 'foo.template', q(
+ gen %1
+ );
+
+could be used in a case where the command C<gen> creates two files, both
+F<foo.h> and F<foo.c>.
+
+
+=head2 The C<Objects> method
+
+The C<Objects> method arranges to create the object files that correspond to
+the specified source files. It is invoked as shown below:
+
+ @files = Objects $env <source or object files>;
+
+Under Unix, source files ending in F<.s> and F<.c> are currently
+supported, and will be compiled into a name of the same file ending
+in F<.o>. By default, all files are created by invoking the external
+command which results from expanding the C<CCCOM> construction variable,
+with C<%E<lt>> and C<%E<gt>> set to the source and object files,
+respectively. (See the section above on construction variable expansion
+for details). The variable C<CPPPATH> is also used when scanning source
+files for dependencies. This is a colon separated list of pathnames, and
+is also used to create the construction variable C<_IFLAGS,> which will
+contain the appropriate list of -C<I> options for the compilation. Any
+relative pathnames in C<CPPPATH> is interpreted relative to the
+directory in which the associated construction environment was created
+(absolute and top-relative names may also be used). This variable is
+used by C<CCCOM>. The behavior of this command can be modified by
+changing any of the variables which are interpolated into C<CCCOM>, such
+as C<CC>, C<CFLAGS>, and, indirectly, C<CPPPATH>. It's also possible
+to replace the value of C<CCCOM>, itself. As a convenience, this file
+returns the list of object filenames.
+
+
+=head2 The C<Program> method
+
+The C<Program> method arranges to link the specified program with the
+specified object files. It is invoked in the following manner:
+
+ Program $env <program name>, <source or object files>;
+
+The program name will have the value of the C<SUFEXE> construction
+variable appended (by default, C<.exe> on Win32 systems, nothing on Unix
+systems) if the suffix is not already present.
+
+Source files may be specified in place of objects files--the C<Objects>
+method will be invoked to arrange the conversion of all the files into
+object files, and hence all the observations about the C<Objects> method,
+above, apply to this method also.
+
+The actual linking of the program will be handled by an external command
+which results from expanding the C<LINKCOM> construction variable, with
+C<%E<lt>> set to the object files to be linked (in the order presented),
+and C<%E<gt>> set to the target. (See the section above on construction
+variable expansion for details.) The user may set additional variables
+in the construction environment, including C<LINK>, to define which
+program to use for linking, C<LIBPATH>, a colon-separated list of
+library search paths, for use with library specifications of the form
+I<-llib>, and C<LIBS>, specifying the list of libraries to link against
+(in either I<-llib> form or just as pathnames. Relative pathnames in
+both C<LIBPATH> and C<LIBS> are interpreted relative to the directory
+in which the associated construction environment is created (absolute
+and top-relative names may also be used). Cons automatically sets up
+dependencies on any libraries mentioned in C<LIBS>: those libraries will
+be built before the command is linked.
+
+
+=head2 The C<Library> method
+
+The C<Library> method arranges to create the specified library from the
+specified object files. It is invoked as follows:
+
+ Library $env <library name>, <source or object files>;
+
+The library name will have the value of the C<SUFLIB> construction
+variable appended (by default, C<.lib> on Win32 systems, C<.a> on Unix
+systems) if the suffix is not already present.
+
+Source files may be specified in place of objects files--the C<Objects>
+method will be invoked to arrange the conversion of all the files into
+object files, and hence all the observations about the C<Objects> method,
+above, apply to this method also.
+
+The actual creation of the library will be handled by an external
+command which results from expanding the C<ARCOM> construction variable,
+with C<%E<lt>> set to the library members (in the order presented),
+and C<%E<gt>> to the library to be created. (See the section above
+on construction variable expansion for details.) The user may set
+variables in the construction environment which will affect the
+operation of the command. These include C<AR>, the archive program
+to use, C<ARFLAGS>, which can be used to modify the flags given to
+the program specified by C<AR>, and C<RANLIB>, the name of a archive
+index generation program, if needed (if the particular need does not
+require the latter functionality, then C<ARCOM> must be redefined to not
+reference C<RANLIB>).
+
+The C<Library> method allows the same library to be specified in multiple
+method invocations. All of the contributing objects from all the invocations
+(which may be from different directories) are combined and generated by a
+single archive command. Note, however, that if you prune a build so that
+only part of a library is specified, then only that part of the library will
+be generated (the rest will disappear!).
+
+
+=head2 The C<Module> method
+
+The C<Module> method is a combination of the C<Program> and C<Command>
+methods. Rather than generating an executable program directly, this command
+allows you to specify your own command to actually generate a module. The
+method is invoked as follows:
+
+ Module $env <module name>, <source or object files>, <construction command>;
+
+This command is useful in instances where you wish to create, for example,
+dynamically loaded modules, or statically linked code libraries.
+
+
+=head2 The C<Depends> method
+
+The C<Depends> method allows you to specify additional dependencies for a
+target. It is invoked as follows:
+
+ Depends $env <target>, <dependencies>;
+
+This may be occasionally useful, especially in cases where no scanner exists
+(or is writable) for particular types of files. Normally, dependencies are
+calculated automatically from a combination of the explicit dependencies set
+up by the method invocation or by scanning source files.
+
+A set of identical dependencies for multiple targets may be specified
+using a reference to a list of targets. In Perl, a list reference can
+be created by enclosing a list in square brackets. Hence the following
+command:
+
+ Depends $env ['foo', 'bar'], 'input_file_1', 'input_file_2';
+
+specifies that both the F<foo> and F<bar> files depend on the listed
+input files.
+
+
+=head2 The C<RuleSet> method
+
+The C<RuleSet> method returns the construction variables for building
+various components with one of the rule sets supported by Cons. The
+currently supported rule sets are:
+
+=over 4
+
+=item msvc
+
+Rules for the Microsoft Visual C++ compiler suite.
+
+=item unix
+
+Generic rules for most UNIX-like compiler suites.
+
+=back
+
+On systems with more than one available compiler suite, this allows you
+to easily create side-by-side environments for building software with
+multiple tools:
+
+ $msvcenv = new cons(RuleSet("msvc"));
+ $cygnusenv = new cons(RuleSet("unix"));
+
+In the future, this could also be extended to other platforms that
+have different default rule sets.
+
+
+=head2 The C<DefaultRules> method
+
+The C<DefaultRules> method sets the default construction variables that
+will be returned by the C<new> method to the specified arguments:
+
+ DefaultRules(CC => 'gcc',
+ CFLAGS => '',
+ CCCOM => '%CC %CFLAGS %_IFLAGS -c %< -o %>');
+ $env = new cons();
+ # $env now contains *only* the CC, CFLAGS,
+ # and CCCOM construction variables
+
+Combined with the C<RuleSet> method, this also provides an easy way
+to set explicitly the default build environment to use some supported
+toolset other than the Cons defaults:
+
+ # use a UNIX-like tool suite (like cygwin) on Win32
+ DefaultRules(RuleSet('unix'));
+ $env = new cons();
+
+Note that the C<DefaultRules> method completely replaces the default
+construction environment with the specified arguments, it does not
+simply override the existing defaults. To override one or more
+variables in a supported C<RuleSet>, append the variables and values:
+
+ DefaultRules(RuleSet('unix'), CFLAGS => '-O3');
+ $env1 = new cons();
+ $env2 = new cons();
+ # both $env1 and $env2 have 'unix' defaults
+ # with CFLAGS set to '-O3'
+
+
+=head2 The C<Ignore> method
+
+The C<Ignore> method allows you to ignore explicitly dependencies that
+Cons infers on its own. It is invoked as follows:
+
+ Ignore <patterns>;
+
+This can be used to avoid recompilations due to changes in system header
+files or utilities that are known to not affect the generated targets.
+
+If, for example, a program is built in an NFS-mounted directory on
+multiple systems that have different copies of F<stdio.h>, the differences
+will affect the signatures of all derived targets built from source files
+that C<#include E<lt>stdio.hE<gt>>. This will cause all those targets to
+be rebuilt when changing systems. If this is not desirable behavior, then
+the following line will remove the dependencies on the F<stdio.h> file:
+
+ Ignore '^/usr/include/stdio\.h$';
+
+Note that the arguments to the C<Ignore> method are regular expressions,
+so special characters must be escaped and you may wish to anchor the
+beginning or end of the expression with C<^> or C<$> characters.
+
+
+=head2 The C<Salt> method
+
+The C<Salt> method adds a constant value to the signature calculation
+for every derived file. It is invoked as follows:
+
+ Salt $string;
+
+Changing the Salt value will force a complete rebuild of every derived
+file. This can be used to force rebuilds in certain desired
+circumstances. For example,
+
+ Salt `uname -s`;
+
+Would force a complete rebuild of every derived file whenever the
+operating system on which the build is performed (as reported by C<uname
+-s>) changes.
+
+
+=head2 The C<UseCache> method
+
+The C<UseCache> method instructs Cons to maintain a cache of derived
+files, to be shared among separate build trees of the same project.
+
+ UseCache("cache/<buildname>") || warn("cache directory not found");
+
+
+=head2 The C<SourcePath> method
+
+The C<SourcePath> mathod returns the real source path name of a file,
+as opposed to the path name within a build directory. It is invoked
+as follows:
+
+ $path = SourcePath <buildpath>;
+
+
+=head2 The C<ConsPath> method
+
+The C<ConsPath> method returns true if the supplied path is a derivable
+file, and returns undef (false) otherwise.
+It is invoked as follows:
+
+ $result = ConsPath <path>;
+
+
+=head2 The C<SplitPath> method
+
+The C<SplitPath> method looks up multiple path names in a string separated
+by the default path separator for the operating system (':' on UNIX
+systems, ';' on Windows NT), and returns the fully-qualified names.
+It is invoked as follows:
+
+ @paths = SplitPath <pathlist>;
+
+The C<SplitPath> method will convert names prefixed '#' to the
+appropriate top-level build name (without the '#') and will convert
+relative names to top-level names.
+
+
+=head2 The C<DirPath> method
+
+The C<DirPath> method returns the build path name(s) of a directory or
+list of directories. It is invoked as follows:
+
+ $cwd = DirPath <paths>;
+
+The most common use for the C<DirPath> method is:
+
+ $cwd = DirPath '.';
+
+to fetch the path to the current directory of a subsidiary F<Conscript>
+file.
+
+
+=head2 The C<FilePath> method
+
+The C<FilePath> method returns the build path name(s) of a file or
+list of files. It is invoked as follows:
+
+ $file = FilePath <path>;
+
+
+=head2 The C<Help> method
+
+The C<Help> method specifies help text that will be displayed when the
+user invokes C<cons -h>. This can be used to provide documentation
+of specific targets, values, build options, etc. for the build tree.
+It is invoked as follows:
+
+ Help <helptext>;
+
+The C<Help> method may only be called once, and should typically be
+specified in the top-level F<Construct> file.
+
+
+=head1 Extending Cons
+
+
+=head2 Overriding construction variables
+
+There are several ways of extending Cons, which vary in degree of
+difficulty. The simplest method is to define your own construction
+environment, based on the default environment, but modified to reflect your
+particular needs. This will often suffice for C-based applications. You can
+use the C<new> constructor, and the C<clone> and C<copy> methods to create
+hybrid environments. These changes can be entirely transparent to the
+underlying F<Conscript> files.
+
+
+=head2 Adding new methods
+
+For slightly more demanding changes, you may wish to add new methods to the
+C<cons> package. Here's an example of a very simple extension,
+C<InstallScript>, which installs a tcl script in a requested location, but
+edits the script first to reflect a platform-dependent path that needs to be
+installed in the script:
+
+ # cons::InstallScript - Create a platform dependent version of a shell
+ # script by replacing string ``#!your-path-here'' with platform specific
+ # path $BIN_DIR.
+
+ sub cons::InstallScript {
+ my ($env, $dst, $src) = @_;
+ Command $env $dst, $src, qq(
+ sed s+your-path-here+$BIN_DIR+ %< > %>
+ chmod oug+x %>
+ );
+ }
+
+Notice that this method is defined directly in the C<cons> package (by
+prefixing the name with C<cons::>). A change made in this manner will be
+globally visible to all environments, and could be called as in the
+following example:
+
+ InstallScript $env "$BIN/foo", "foo.tcl";
+
+For a small improvement in generality, the C<BINDIR> variable could be
+passed in as an argument or taken from the construction environment--as
+C<%BINDIR>.
+
+
+=head2 Overriding methods
+
+Instead of adding the method to the C<cons> name space, you could define a
+new package which inherits existing methods from the C<cons> package and
+overrides or adds others. This can be done using Perl's inheritance
+mechanisms.
+
+The following example defines a new package C<cons::switch> which
+overrides the standard C<Library> method. The overridden method builds
+linked library modules, rather than library archives. A new
+constructor is provided. Environments created with this constructor
+will have the new library method; others won't.
+
+ package cons::switch;
+ BEGIN {@ISA = 'cons'}
+
+ sub new {
+ shift;
+ bless new cons(@_);
+ }
+
+ sub Library {
+ my($env) = shift;
+ my($lib) = shift;
+ my(@objs) = Objects $env @_;
+ Command $env $lib, @objs, q(
+ %LD -r %LDFLAGS %< -o %>
+ );
+ }
+
+This functionality could be invoked as in the following example:
+
+ $env = new cons::switch(@overrides);
+ ...
+ Library $env 'lib.o', 'foo.c', 'bar.c';
+
+
+=head1 Invoking Cons
+
+The C<cons> command is usually invoked from the root of the build tree. A
+F<Construct> file must exist in that directory. If the C<-f> argument is
+used, then an alternate F<Construct> file may be used (and, possibly, an
+alternate root, since C<cons> will cd to F<Construct> file's containing
+directory).
+
+If C<cons> is invoked from a child of the root of the build tree with
+the C<-t> argument, it will walk up the directory hierarchy looking for a
+F<Construct> file. (An alternate name may still be specified with C<-f>.)
+The targets supplied on the command line will be modified to be relative
+to the discovered F<Construct> file. For example, from a directory
+containing a top-level F<Construct> file, the following invocation:
+
+ % cd libfoo/subdir
+ % cons -t target
+
+is exactly equivalent to:
+
+ % cons libfoo/subdir/target
+
+If there are any C<Default> targets specified in the directory hierarchy's
+F<Construct> or F<Conscript> files, only the default targets at or below
+the directory from which C<cons -t> was invoked will be built.
+
+The command is invoked as follows:
+
+ cons <arguments> -- <construct-args>
+
+where I<arguments> can be any of the following, in any order:
+
+=over 10
+
+=item I<target>
+
+Build the specified target. If I<target> is a directory, then recursively
+build everything within that directory.
+
+=item I<+pattern>
+
+Limit the F<Conscript> files considered to just those that match I<pattern>,
+which is a Perl regular expression. Multiple C<+> arguments are accepted.
+
+=item I<name>=<val>
+
+Sets I<name> to value I<val> in the C<ARG> hash passed to the top-level
+F<Construct> file.
+
+=item C<-cc>
+
+Show command that would have been executed, when retrieving from cache. No
+indication that the file has been retrieved is given; this is useful for
+generating build logs that can be compared with real build logs.
+
+=item C<-cd>
+
+Disable all caching. Do not retrieve from cache nor flush to cache.
+
+=item C<-cr>
+
+Build dependencies in random order. This is useful when building multiple
+similar trees with caching enabled.
+
+=item C<-cs>
+
+Synchronize existing build targets that are found to be up-to-date with
+cache. This is useful if caching has been disabled with -cc or just recently
+enabled with UseCache.
+
+=item C<-d>
+
+Enable dependency debugging.
+
+=item C<-f> <file>
+
+Use the specified file instead of F<Construct> (but first change to
+containing directory of I<file>).
+
+=item C<-h>
+
+Show a help message local to the current build if one such is defined, and
+exit.
+
+=item C<-k>
+
+Keep going as far as possible after errors.
+
+=item C<-o> <file>
+
+Read override file I<file>.
+
+=item C<-p>
+
+Show construction products in specified trees. No build is attempted.
+
+=item C<-pa>
+
+Show construction products and associated actions. No build is attempted.
+
+=item C<-pw>
+
+Show products and where they are defined. No build is attempted.
+
+=item C<-q>
+
+Make the build quiet. Multiple C<-q> options may be specified.
+
+A single C<-q> options suppress messages about Installing and Removing
+targets.
+
+Two C<-q> options suppress build command lines and target up-to-date
+messages.
+
+=item C<-r>
+
+Remove construction products associated with <targets>. No build is
+attempted.
+
+=item C<-R> <repos>
+
+Search for files in I<repos>. Multiple B<-R> I<repos> directories are
+searched in the order specified.
+
+=item C<-S> <pkg>
+
+Use the sig::<pkg> package to calculate. Supported <pkg> values
+include "md5" for MD5 signature calculation and "md5::debug" for debug
+information about MD5 signature calculation.
+
+If the specified package ends in <::debug>, signature debug information
+will be printed to the file name specified in the C<CONS_SIG_DEBUG>
+environment variable, or to standard output if the environment variable
+is not set.
+
+=item C<-t>
+
+Traverse up the directory hierarchy looking for a F<Construct> file,
+if none exists in the current directory. Targets will be modified to
+be relative to the F<Construct> file.
+
+Internally, C<cons> will change its working directory to the directory
+which contains the top-level F<Construct> file and report:
+
+ cons: Entering directory `top-level-directory'
+
+This message indicates to an invoking editor (such as emacs) or build
+environment that Cons will now report all file names relative to the
+top-level directory. This message can not be suppressed with the C<-q>
+option.
+
+=item C<-v>
+
+Show C<cons> version and continue processing.
+
+=item C<-V>
+
+Show C<cons> version and exit.
+
+=item C<-wf> <file>
+
+Write all filenames considered into I<file>.
+
+=item C<-x>
+
+Show a help message similar to this one, and exit.
+
+=back
+
+And I<construct-args> can be any arguments that you wish to process in the
+F<Construct> file. Note that there should be a B<--> separating the arguments
+to cons and the arguments that you wish to process in the F<Construct> file.
+
+Processing of I<construct-args> can be done by any standard package like
+B<Getopt> or its variants, or any user defined package. B<cons> will pass in
+the I<construct-args> as B<@ARGV> and will not attempt to interpret anything
+after the B<-->.
+
+ % cons -R /usr/local/repository -d os=solaris +driver -- -c test -f DEBUG
+
+would pass the following to cons
+
+ -R /usr/local/repository -d os=solaris +driver
+
+and the following, to the top level F<Construct> file as B<@ARGV>
+
+ -c test -f DEBUG
+
+Note that C<cons -r .> is equivalent to a full recursive C<make clean>,
+but requires no support in the F<Construct> file or any F<Conscript>
+files. This is most useful if you are compiling files into source
+directories (if you separate the F<build> and F<export> directories,
+then you can just remove the directories).
+
+The options C<-p>, C<-pa>, and C<-pw> are extremely useful for use as an aid
+in reading scripts or debugging them. If you want to know what script
+installs F<export/include/foo.h>, for example, just type:
+
+ % cons -pw export/include/foo.h
+
+
+=head1 Using and writing dependency scanners
+
+QuickScan allows simple target-independent scanners to be set up for
+source files. Only one QuickScan scanner may be associated with any given
+source file and environment, although the same scanner may (and should)
+be used for multiple files of a given type.
+
+A QuickScan scanner is only ever invoked once for a given source file,
+and it is only invoked if the file is used by some target in the tree
+(i.e., there is a dependency on the source file).
+
+QuickScan is invoked as follows:
+
+ QuickScan CONSENV CODEREF, FILENAME [, PATH]
+
+The subroutine referenced by CODEREF is expected to return a list of
+filenames included directly by FILE. These filenames will, in turn, be
+scanned. The optional PATH argument supplies a lookup path for finding
+FILENAME and/or files returned by the user-supplied subroutine. The PATH
+may be a reference to an array of lookup-directory names, or a string of
+names separated by the system's separator character (':' on UNIX systems,
+';' on Windows NT).
+
+The subroutine is called once for each line in the file, with $_ set to the
+current line. If the subroutine needs to look at additional lines, or, for
+that matter, the entire file, then it may read them itself, from the
+filehandle SCAN. It may also terminate the loop, if it knows that no further
+include information is available, by closing the filehandle.
+
+Whether or not a lookup path is provided, QuickScan first tries to lookup
+the file relative to the current directory (for the top-level file
+supplied directly to QuickScan), or from the directory containing the
+file which referenced the file. This is not very general, but seems good
+enough--especially if you have the luxury of writing your own utilities
+and can control the use of the search path in a standard way.
+
+Here's a real example, taken from a F<Construct> file here:
+
+ sub cons::SMFgen {
+ my($env, @tables) = @_;
+ foreach $t (@tables) {
+ $env->QuickScan(sub { /\b\S*?\.smf\b/g }, "$t.smf",
+ $env->{SMF_INCLUDE_PATH});
+ $env->Command(["$t.smdb.cc","$t.smdb.h","$t.snmp.cc",
+ "$t.ami.cc", "$t.http.cc"], "$t.smf",
+ q(smfgen %( %SMF_INCLUDE_OPT %) %<));
+ }
+ }
+
+The subroutine above finds all names of the form <name>.smf in the
+file. It will return the names even if they're found within comments,
+but that's OK (the mechanism is forgiving of extra files; they're just
+ignored on the assumption that the missing file will be noticed when
+the program, in this example, smfgen, is actually invoked).
+
+[NOTE that the form C<$env-E<gt>QuickScan ...> and C<$env-E<gt>Command
+...> should not be necessary, but, for some reason, is required
+for this particular invocation. This appears to be a bug in Perl or
+a misunderstanding on my part; this invocation style does not always
+appear to be necessary.]
+
+Here is another way to build the same scanner. This one uses an
+explicit code reference, and also (unnecessarily, in this case) reads
+the whole file itself:
+
+ sub myscan {
+ my(@includes);
+ do {
+ push(@includes, /\b\S*?\.smf\b/g);
+ } while <SCAN>;
+ @includes
+ }
+
+Note that the order of the loop is reversed, with the loop test at the
+end. This is because the first line is already read for you. This scanner
+can be attached to a source file by:
+
+ QuickScan $env \&myscan, "$_.smf";
+
+This final example, which scans a different type of input file, takes
+over the file scanning rather than being called for each input line:
+
+ $env->QuickScan(
+ sub { my(@includes) = ();
+ do {
+ push(@includes, $3)
+ if /^(#include|import)\s+(\")(.+)(\")/ && $3
+ } while <SCAN>;
+ @includes
+ },
+ "$idlFileName",
+ "$env->{CPPPATH};$BUILD/ActiveContext/ACSCLientInterfaces"
+ );
+
+=head1 SUPPORT AND SUGGESTIONS
+
+Cons is maintained by the user community. To subscribe, send mail to
+B<cons-discuss-request@gnu.org> with body B<subscribe>.
+
+Please report any suggestions through the B<cons-discuss@gnu.org> mailing
+list.
+
+=head1 BUGS
+
+Sure to be some. Please report any bugs through the B<bug-cons@gnu.org>
+mailing list.
+
+=head1 INFORMATION ABOUT CONS
+
+Information about CONS can be obtained from the official cons web site
+B<http://www.dsmit.com/cons/> or its mirrors listed there.
+
+The cons maintainers can be contacted by email at
+B<cons-maintainers@gnu.org>
+
+=head1 AUTHORS
+
+Originally by Bob Sidebotham. Then significantly enriched by the members
+of the Cons community B<cons-discuss@gnu.org>.
+
+The Cons community would like to thank Ulrich Pfeifer for the original pod
+documentation derived from the F<cons.html> file. Cons documentation is now
+a part of the program itself.
+
+=cut
+
diff --git a/code/unix/q3test.spec.sh b/code/unix/q3test.spec.sh
new file mode 100755
index 0000000..93f3714
--- /dev/null
+++ b/code/unix/q3test.spec.sh
@@ -0,0 +1,52 @@
+#!/bin/sh
+# Generate Quake3 test
+# $1 is version
+# $2 is release
+# $3 is arch
+# $4 is install dir (assumed to be in /var/tmp)
+cat <<EOF
+%define name q3test
+%define version ${1}
+%define release ${2}
+%define arch ${3}
+%define builddir \$RPM_BUILD_DIR/%{name}-%{version}
+Name: %{name}
+Version: %{version}
+Release: %{release}
+Vendor: id Software
+Packager: Dave "Zoid" Kirsch <zoid@idsoftware.com>
+URL: http://www.idsoftware.com/
+Source: q3test-%{version}.tar.gz
+Group: Games
+Copyright: Restricted
+Icon: quake3.gif
+BuildRoot: /var/tmp/%{name}-%{version}
+Summary: Q3Test for Linux
+
+%description
+
+%install
+
+%files
+
+%attr(644,root,root) $4/README.EULA
+%attr(644,root,root) $4/README.Q3Test
+%attr(644,root,root) $4/README.Linux
+%attr(644,root,root) $4/Quake3.kdelnk
+%attr(644,root,root) $4/quake3.xpm
+%attr(755,root,root) $4/linuxquake3
+%attr(755,root,root) $4/cgamei386.so
+%attr(755,root,root) $4/qagamei386.so
+%attr(755,root,root) $4/uii386.so
+%attr(755,root,root) $4/libMesaVoodooGL.so.3.1
+%attr(644,root,root) $4/demoq3/pak0.pk3
+
+%post
+
+if [ -n "\$KDEDIR" ]; then
+ ln -sf $4/Quake3.kdelnk \$KDEDIR/share/applnk/Games/Quake3.kdelnk
+ ln -sf $4/quake3.xpm \$KDEDIR/share/icons/quake3.xpm
+fi
+
+EOF
+
diff --git a/code/unix/qasm.h b/code/unix/qasm.h
new file mode 100755
index 0000000..365346f
--- /dev/null
+++ b/code/unix/qasm.h
@@ -0,0 +1,480 @@
+/*
+===========================================================================
+Copyright (C) 1999-2005 Id Software, Inc.
+
+This file is part of Quake III Arena source code.
+
+Quake III Arena source code is free software; you can redistribute it
+and/or modify it under the terms of the GNU General Public License as
+published by the Free Software Foundation; either version 2 of the License,
+or (at your option) any later version.
+
+Quake III Arena source code is distributed in the hope that it will be
+useful, but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with Foobar; if not, write to the Free Software
+Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+===========================================================================
+*/
+#ifndef __ASM_I386__
+#define __ASM_I386__
+
+#ifdef ELF
+#define C(label) label
+#else
+#define C(label) _##label
+#endif
+
+
+//#define GLQUAKE 1
+
+#if defined(_WIN32) && !defined(WINDED)
+
+#if defined(_M_IX86)
+#define __i386__ 1
+#endif
+
+#endif
+
+#ifdef __i386__
+#define id386 1
+#else
+#define id386 0
+#endif
+
+// !!! must be kept the same as in d_iface.h !!!
+#define TRANSPARENT_COLOR 255
+
+#ifndef GLQUAKE
+ .extern C(d_zistepu)
+ .extern C(d_pzbuffer)
+ .extern C(d_zistepv)
+ .extern C(d_zrowbytes)
+ .extern C(d_ziorigin)
+ .extern C(r_turb_s)
+ .extern C(r_turb_t)
+ .extern C(r_turb_pdest)
+ .extern C(r_turb_spancount)
+ .extern C(r_turb_turb)
+ .extern C(r_turb_pbase)
+ .extern C(r_turb_sstep)
+ .extern C(r_turb_tstep)
+ .extern C(r_bmodelactive)
+ .extern C(d_sdivzstepu)
+ .extern C(d_tdivzstepu)
+ .extern C(d_sdivzstepv)
+ .extern C(d_tdivzstepv)
+ .extern C(d_sdivzorigin)
+ .extern C(d_tdivzorigin)
+ .extern C(sadjust)
+ .extern C(tadjust)
+ .extern C(bbextents)
+ .extern C(bbextentt)
+ .extern C(cacheblock)
+ .extern C(d_viewbuffer)
+ .extern C(cachewidth)
+ .extern C(d_pzbuffer)
+ .extern C(d_zrowbytes)
+ .extern C(d_zwidth)
+ .extern C(d_scantable)
+ .extern C(r_lightptr)
+ .extern C(r_numvblocks)
+ .extern C(prowdestbase)
+ .extern C(pbasesource)
+ .extern C(r_lightwidth)
+ .extern C(lightright)
+ .extern C(lightrightstep)
+ .extern C(lightdeltastep)
+ .extern C(lightdelta)
+ .extern C(lightright)
+ .extern C(lightdelta)
+ .extern C(sourcetstep)
+ .extern C(surfrowbytes)
+ .extern C(lightrightstep)
+ .extern C(lightdeltastep)
+ .extern C(r_sourcemax)
+ .extern C(r_stepback)
+ .extern C(colormap)
+ .extern C(blocksize)
+ .extern C(sourcesstep)
+ .extern C(lightleft)
+ .extern C(blockdivshift)
+ .extern C(blockdivmask)
+ .extern C(lightleftstep)
+ .extern C(r_origin)
+ .extern C(r_ppn)
+ .extern C(r_pup)
+ .extern C(r_pright)
+ .extern C(ycenter)
+ .extern C(xcenter)
+ .extern C(d_vrectbottom_particle)
+ .extern C(d_vrectright_particle)
+ .extern C(d_vrecty)
+ .extern C(d_vrectx)
+ .extern C(d_pix_shift)
+ .extern C(d_pix_min)
+ .extern C(d_pix_max)
+ .extern C(d_y_aspect_shift)
+ .extern C(screenwidth)
+ .extern C(r_leftclipped)
+ .extern C(r_leftenter)
+ .extern C(r_rightclipped)
+ .extern C(r_rightenter)
+ .extern C(modelorg)
+ .extern C(xscale)
+ .extern C(r_refdef)
+ .extern C(yscale)
+ .extern C(r_leftexit)
+ .extern C(r_rightexit)
+ .extern C(r_lastvertvalid)
+ .extern C(cacheoffset)
+ .extern C(newedges)
+ .extern C(removeedges)
+ .extern C(r_pedge)
+ .extern C(r_framecount)
+ .extern C(r_u1)
+ .extern C(r_emitted)
+ .extern C(edge_p)
+ .extern C(surface_p)
+ .extern C(surfaces)
+ .extern C(r_lzi1)
+ .extern C(r_v1)
+ .extern C(r_ceilv1)
+ .extern C(r_nearzi)
+ .extern C(r_nearzionly)
+ .extern C(edge_aftertail)
+ .extern C(edge_tail)
+ .extern C(current_iv)
+ .extern C(edge_head_u_shift20)
+ .extern C(span_p)
+ .extern C(edge_head)
+ .extern C(fv)
+ .extern C(edge_tail_u_shift20)
+ .extern C(r_apverts)
+ .extern C(r_anumverts)
+ .extern C(aliastransform)
+ .extern C(r_avertexnormals)
+ .extern C(r_plightvec)
+ .extern C(r_ambientlight)
+ .extern C(r_shadelight)
+ .extern C(aliasxcenter)
+ .extern C(aliasycenter)
+ .extern C(a_sstepxfrac)
+ .extern C(r_affinetridesc)
+ .extern C(acolormap)
+ .extern C(d_pcolormap)
+ .extern C(r_affinetridesc)
+ .extern C(d_sfrac)
+ .extern C(d_ptex)
+ .extern C(d_pedgespanpackage)
+ .extern C(d_tfrac)
+ .extern C(d_light)
+ .extern C(d_zi)
+ .extern C(d_pdest)
+ .extern C(d_pz)
+ .extern C(d_aspancount)
+ .extern C(erroradjustup)
+ .extern C(errorterm)
+ .extern C(d_xdenom)
+ .extern C(r_p0)
+ .extern C(r_p1)
+ .extern C(r_p2)
+ .extern C(a_tstepxfrac)
+ .extern C(r_sstepx)
+ .extern C(r_tstepx)
+ .extern C(a_ststepxwhole)
+ .extern C(zspantable)
+ .extern C(skintable)
+ .extern C(r_zistepx)
+ .extern C(erroradjustdown)
+ .extern C(d_countextrastep)
+ .extern C(ubasestep)
+ .extern C(a_ststepxwhole)
+ .extern C(a_tstepxfrac)
+ .extern C(r_lstepx)
+ .extern C(a_spans)
+ .extern C(erroradjustdown)
+ .extern C(d_pdestextrastep)
+ .extern C(d_pzextrastep)
+ .extern C(d_sfracextrastep)
+ .extern C(d_ptexextrastep)
+ .extern C(d_countextrastep)
+ .extern C(d_tfracextrastep)
+ .extern C(d_lightextrastep)
+ .extern C(d_ziextrastep)
+ .extern C(d_pdestbasestep)
+ .extern C(d_pzbasestep)
+ .extern C(d_sfracbasestep)
+ .extern C(d_ptexbasestep)
+ .extern C(ubasestep)
+ .extern C(d_tfracbasestep)
+ .extern C(d_lightbasestep)
+ .extern C(d_zibasestep)
+ .extern C(zspantable)
+ .extern C(r_lstepy)
+ .extern C(r_sstepy)
+ .extern C(r_tstepy)
+ .extern C(r_zistepy)
+ .extern C(D_PolysetSetEdgeTable)
+ .extern C(D_RasterizeAliasPolySmooth)
+
+ .extern float_point5
+ .extern Float2ToThe31nd
+ .extern izistep
+ .extern izi
+ .extern FloatMinus2ToThe31nd
+ .extern float_1
+ .extern float_particle_z_clip
+ .extern float_minus_1
+ .extern float_0
+ .extern fp_16
+ .extern fp_64k
+ .extern fp_1m
+ .extern fp_1m_minus_1
+ .extern fp_8
+ .extern entryvec_table
+ .extern advancetable
+ .extern sstep
+ .extern tstep
+ .extern pspantemp
+ .extern counttemp
+ .extern jumptemp
+ .extern reciprocal_table
+ .extern DP_Count
+ .extern DP_u
+ .extern DP_v
+ .extern DP_32768
+ .extern DP_Color
+ .extern DP_Pix
+ .extern DP_EntryTable
+ .extern pbase
+ .extern s
+ .extern t
+ .extern sfracf
+ .extern tfracf
+ .extern snext
+ .extern tnext
+ .extern spancountminus1
+ .extern zi16stepu
+ .extern sdivz16stepu
+ .extern tdivz16stepu
+ .extern zi8stepu
+ .extern sdivz8stepu
+ .extern tdivz8stepu
+ .extern reciprocal_table_16
+ .extern entryvec_table_16
+ .extern ceil_cw
+ .extern single_cw
+ .extern fp_64kx64k
+ .extern pz
+ .extern spr8entryvec_table
+#endif
+
+ .extern C(snd_scaletable)
+ .extern C(paintbuffer)
+ .extern C(snd_linear_count)
+ .extern C(snd_p)
+ .extern C(snd_vol)
+ .extern C(snd_out)
+ .extern C(vright)
+ .extern C(vup)
+ .extern C(vpn)
+ .extern C(BOPS_Error)
+
+//
+// !!! note that this file must match the corresponding C structures at all
+// times !!!
+//
+
+// plane_t structure
+// !!! if this is changed, it must be changed in model.h too !!!
+// !!! if the size of this is changed, the array lookup in SV_HullPointContents
+// must be changed too !!!
+#define pl_normal 0
+#define pl_dist 12
+#define pl_type 16
+#define pl_signbits 17
+#define pl_pad 18
+#define pl_size 20
+
+// hull_t structure
+// !!! if this is changed, it must be changed in model.h too !!!
+#define hu_clipnodes 0
+#define hu_planes 4
+#define hu_firstclipnode 8
+#define hu_lastclipnode 12
+#define hu_clip_mins 16
+#define hu_clip_maxs 28
+#define hu_size 40
+
+// dnode_t structure
+// !!! if this is changed, it must be changed in bspfile.h too !!!
+#define nd_planenum 0
+#define nd_children 4
+#define nd_mins 8
+#define nd_maxs 20
+#define nd_firstface 32
+#define nd_numfaces 36
+#define nd_size 40
+
+// sfxcache_t structure
+// !!! if this is changed, it much be changed in sound.h too !!!
+#define sfxc_length 0
+#define sfxc_loopstart 4
+#define sfxc_speed 8
+#define sfxc_width 12
+#define sfxc_stereo 16
+#define sfxc_data 20
+
+// channel_t structure
+// !!! if this is changed, it much be changed in sound.h too !!!
+#define ch_sfx 0
+#define ch_leftvol 4
+#define ch_rightvol 8
+#define ch_end 12
+#define ch_pos 16
+#define ch_looping 20
+#define ch_entnum 24
+#define ch_entchannel 28
+#define ch_origin 32
+#define ch_dist_mult 44
+#define ch_master_vol 48
+#define ch_size 52
+
+// portable_samplepair_t structure
+// !!! if this is changed, it much be changed in sound.h too !!!
+#define psp_left 0
+#define psp_right 4
+#define psp_size 8
+
+
+//
+// !!! note that this file must match the corresponding C structures at all
+// times !!!
+//
+
+// !!! if this is changed, it must be changed in r_local.h too !!!
+#define NEAR_CLIP 0.01
+
+// !!! if this is changed, it must be changed in r_local.h too !!!
+#define CYCLE 128
+
+// espan_t structure
+// !!! if this is changed, it must be changed in r_shared.h too !!!
+#define espan_t_u 0
+#define espan_t_v 4
+#define espan_t_count 8
+#define espan_t_pnext 12
+#define espan_t_size 16
+
+// sspan_t structure
+// !!! if this is changed, it must be changed in d_local.h too !!!
+#define sspan_t_u 0
+#define sspan_t_v 4
+#define sspan_t_count 8
+#define sspan_t_size 12
+
+// spanpackage_t structure
+// !!! if this is changed, it must be changed in d_polyset.c too !!!
+#define spanpackage_t_pdest 0
+#define spanpackage_t_pz 4
+#define spanpackage_t_count 8
+#define spanpackage_t_ptex 12
+#define spanpackage_t_sfrac 16
+#define spanpackage_t_tfrac 20
+#define spanpackage_t_light 24
+#define spanpackage_t_zi 28
+#define spanpackage_t_size 32
+
+// edge_t structure
+// !!! if this is changed, it must be changed in r_shared.h too !!!
+#define et_u 0
+#define et_u_step 4
+#define et_prev 8
+#define et_next 12
+#define et_surfs 16
+#define et_nextremove 20
+#define et_nearzi 24
+#define et_owner 28
+#define et_size 32
+
+// surf_t structure
+// !!! if this is changed, it must be changed in r_shared.h too !!!
+#define SURF_T_SHIFT 6
+#define st_next 0
+#define st_prev 4
+#define st_spans 8
+#define st_key 12
+#define st_last_u 16
+#define st_spanstate 20
+#define st_flags 24
+#define st_data 28
+#define st_entity 32
+#define st_nearzi 36
+#define st_insubmodel 40
+#define st_d_ziorigin 44
+#define st_d_zistepu 48
+#define st_d_zistepv 52
+#define st_pad 56
+#define st_size 64
+
+// clipplane_t structure
+// !!! if this is changed, it must be changed in r_local.h too !!!
+#define cp_normal 0
+#define cp_dist 12
+#define cp_next 16
+#define cp_leftedge 20
+#define cp_rightedge 21
+#define cp_reserved 22
+#define cp_size 24
+
+// medge_t structure
+// !!! if this is changed, it must be changed in model.h too !!!
+#define me_v 0
+#define me_cachededgeoffset 4
+#define me_size 8
+
+// mvertex_t structure
+// !!! if this is changed, it must be changed in model.h too !!!
+#define mv_position 0
+#define mv_size 12
+
+// refdef_t structure
+// !!! if this is changed, it must be changed in render.h too !!!
+#define rd_vrect 0
+#define rd_aliasvrect 20
+#define rd_vrectright 40
+#define rd_vrectbottom 44
+#define rd_aliasvrectright 48
+#define rd_aliasvrectbottom 52
+#define rd_vrectrightedge 56
+#define rd_fvrectx 60
+#define rd_fvrecty 64
+#define rd_fvrectx_adj 68
+#define rd_fvrecty_adj 72
+#define rd_vrect_x_adj_shift20 76
+#define rd_vrectright_adj_shift20 80
+#define rd_fvrectright_adj 84
+#define rd_fvrectbottom_adj 88
+#define rd_fvrectright 92
+#define rd_fvrectbottom 96
+#define rd_horizontalFieldOfView 100
+#define rd_xOrigin 104
+#define rd_yOrigin 108
+#define rd_vieworg 112
+#define rd_viewangles 124
+#define rd_ambientlight 136
+#define rd_size 140
+
+// mtriangle_t structure
+// !!! if this is changed, it must be changed in model.h too !!!
+#define mtri_facesfront 0
+#define mtri_vertindex 4
+#define mtri_size 16 // !!! if this changes, array indexing in !!!
+ // !!! d_polysa.s must be changed to match !!!
+#define mtri_shift 4
+
+#endif
diff --git a/code/unix/quake3.gif b/code/unix/quake3.gif
new file mode 100755
index 0000000..0e7a01c
--- /dev/null
+++ b/code/unix/quake3.gif
Binary files differ
diff --git a/code/unix/quake3.xpm b/code/unix/quake3.xpm
new file mode 100755
index 0000000..c9b736d
--- /dev/null
+++ b/code/unix/quake3.xpm
@@ -0,0 +1,161 @@
+/* XPM */
+static char *quake3[] = {
+/* width height num_colors chars_per_pixel */
+" 32 32 122 2",
+/* colors */
+".. c #6b1008",
+".# c #6b1810",
+".a c #6b2118",
+".b c #6b2921",
+".c c #6b3129",
+".d c #731810",
+".e c #732118",
+".f c #732921",
+".g c #733129",
+".h c #733931",
+".i c #733939",
+".j c #734239",
+".k c #734a4a",
+".l c #7b1810",
+".m c #7b2118",
+".n c #7b2921",
+".o c #7b4239",
+".p c #7b4a4a",
+".q c #7b524a",
+".r c #7b5252",
+".s c #7b5a5a",
+".t c #7b6363",
+".u c #7b6b6b",
+".v c #7b7373",
+".w c #842110",
+".x c #842118",
+".y c #842921",
+".z c #843129",
+".A c #843931",
+".B c #844239",
+".C c #844a42",
+".D c #84524a",
+".E c #845252",
+".F c #845a5a",
+".G c #846b63",
+".H c #846b6b",
+".I c #847373",
+".J c #847b7b",
+".K c #848484",
+".L c #8c2118",
+".M c #8c2921",
+".N c #8c4239",
+".O c #8c4a42",
+".P c #8c5a52",
+".Q c #8c635a",
+".R c #8c6363",
+".S c #8c6b6b",
+".T c #8c7b7b",
+".U c #8c8484",
+".V c #8c848c",
+".W c #8c8c8c",
+".X c #942921",
+".Y c #944a42",
+".Z c #946363",
+".0 c #946b63",
+".1 c #947373",
+".2 c #947b7b",
+".3 c #948484",
+".4 c #949494",
+".5 c #9c5a5a",
+".6 c #9c6363",
+".7 c #9c6b63",
+".8 c #9c736b",
+".9 c #9c7b73",
+"#. c #9c7b7b",
+"## c #9c8c8c",
+"#a c #9c9494",
+"#b c #9c949c",
+"#c c #9c9c9c",
+"#d c #a55a52",
+"#e c #a5736b",
+"#f c #a57b73",
+"#g c #a57b7b",
+"#h c #a58484",
+"#i c #a58c8c",
+"#j c #a59c9c",
+"#k c #a5a5a5",
+"#l c #ad8c84",
+"#m c #ad8c8c",
+"#n c #ad9494",
+"#o c #ada5a5",
+"#p c #adadad",
+"#q c #b59c9c",
+"#r c #b5a5a5",
+"#s c #b5adad",
+"#t c #b5b5b5",
+"#u c #bda5a5",
+"#v c #bdadad",
+"#w c #bdb5b5",
+"#x c #bdbdbd",
+"#y c #c69c9c",
+"#z c #c6a59c",
+"#A c #c6adad",
+"#B c #c6b5ad",
+"#C c #c6b5b5",
+"#D c #c6bdbd",
+"#E c #c6c6c6",
+"#F c #ceb5b5",
+"#G c #cec6c6",
+"#H c #cecece",
+"#I c #d6b5b5",
+"#J c #d6bdbd",
+"#K c #d6c6bd",
+"#L c #d6cece",
+"#M c #d6d6d6",
+"#N c #decec6",
+"#O c #decece",
+"#P c #ded6ce",
+"#Q c #ded6d6",
+"#R c #dedede",
+"#S c #e7d6d6",
+"#T c #e7dede",
+"#U c #e7e7e7",
+"#V c #efe7e7",
+"#W c #efefe7",
+"#X c #efefef",
+"#Y c #f7efef",
+"#Z c #f7f7ef",
+"#0 c #f7f7f7",
+"#1 c #fff7f7",
+"#2 c #fffff7",
+"#3 c #ffffff",
+/* pixels */
+"#3#2#3#0#3#2#3#2#3#0#3#2#3#2#3#0#3#2#3#2#3#0#3#2#3#2#3#0#3#2#3#2",
+"#3#2#3#2#3#0#3#2#3#2#3#0#3#2#Z#A#3#2#3#0#3#2#3#2#3#0#3#2#3#2#3#0",
+"#3#2#3#0#3#3#2#3#0#3#3#2#3#3#X#l#U#0#3#2#3#3#0#3#2#3#3#2#3#0#3#3",
+"#3#2#3#2#3#0#3#3#2#3#2#3#0#3#Y#e#G#0#3#2#3#2#3#3#0#3#2#3#2#3#0#3",
+"#3#2#3#0#3#3#2#3#0#3#3#2#3#2#U.6#D#X#3#2#3#3#0#3#2#3#3#0#3#2#3#2",
+"#3#2#3#2#3#0#3#2#3#2#3#3#0#3#Q#d#t#X#3#2#3#2#3#0#3#2#3#3#2#3#0#3",
+"#3#2#3#0#3#3#2#3#0#3#2#3#3#2#H.Y#t#X#2#3#1#3#3#2#3#2#3#0#3#3#2#3",
+"#3#2#3#2#3#0#3#3#2#3#2#3#0#3#G.N#s#X#3#2#3#2#3#3#0#3#2#3#2#3#3#0",
+"#3#2#3#0#3#3#2#3#0#3#3#2#3#2#A.A#o#W#3#0#3#3#2#3#2#3#3#0#3#2#3#2",
+"#3#Z#O#s#R#2#3#2#3#2#3#3#0#3#r.z#k#U#2#3#0#3#2#3#2#T#F#L#X#2#3#0",
+"#O.2#B#V#X#X#3#2#3#3#0#3#2#3#n.m#a#U#2#3#3#0#3#2#3#3#X#C#.#u#2#3",
+".D#Q#M#H#M#Z#3#3#0#3#2#3#2#3#h.x.W#R#3#2#3#2#3#3#0#3#Z#R#D.C#M#0",
+".7.D#n#R#1#3#2#3#2#3#0#3#3#1#f.x.W#R#2#3#1#3#2#3#2#0#X#F.5.o#s#U",
+"#V.R.b.f.C.7#i#y#F#K#N#O#P#X.8.w.K#D#O#N#J#I#z#h.7.C.f.f.P#v#k#R",
+"#3#R#a.s.h.a.#...d.d.m.y.n#A.Z.x.I.D.y.m.e.#...#.a.i.t###a#c#x#Y",
+"#3#1#W#E#k.T.H.k.j.j.h.y.m.1.C.L.v.o.l.a.b.h.k.u.v.J.W#c#t#M#0#3",
+"#3#2#3#0#Z#R#E#t#b#a.W.H.y.t.p.X.J.o.g.v.J.V.W.4#o#x#M#V#0#3#2#3",
+"#3#2#3#2#3#3#1#0#X#U#R#r.n.I.q.M.J.p.g.U#t#M#R#V#0#0#2#3#2#3#2#3",
+"#3#2#3#0#3#2#3#2#3#2#3#N.z.J.E.x.K.D.h.4#T#3#2#3#2#3#2#3#0#3#3#2",
+"#3#2#3#2#3#3#0#3#2#3#2#O.A.K.Q.m.K.F.o#a#U#3#2#3#0#3#3#2#3#2#3#0",
+"#3#2#3#0#3#2#3#3#2#3#0#S.B.U.T.d.4.0.p#c#X#3#2#3#2#3#0#3#3#2#3#2",
+"#3#2#3#2#3#3#0#3#2#3#3#S.O.4.3.e.4#g.r#p#X#3#2#3#0#3#3#2#3#0#3#3",
+"#3#2#3#0#3#2#3#2#3#0#3#T.C#a.4.b.4#h.G#s#Z#3#3#2#3#0#3#2#3#2#3#2",
+"#3#2#3#2#3#3#0#3#2#3#2#U.F#j#o.c.4#m.T#w#0#3#0#3#2#3#3#2#3#0#3#3",
+"#3#2#3#0#3#2#3#3#2#3#0#Y.S#o#t.j#j#q###x#0#3#2#3#3#0#3#2#3#3#2#3",
+"#3#2#3#2#3#3#0#3#2#3#3#0.9#w#G.p#k#r#o#E#0#3#2#3#2#3#0#3#2#3#2#3",
+"#3#2#3#0#3#2#3#2#3#0#3#2#n#E#Q.Q#p#w#x#G#0#3#2#3#3#2#3#0#3#3#2#3",
+"#3#2#3#2#3#3#0#3#2#3#3#2#R#H#T.3#t#R#T#R#2#3#2#3#0#3#3#2#3#2#3#0",
+"#3#2#3#0#3#2#3#3#2#3#0#3#2#U#U#s#D#0#0#W#3#3#0#3#2#3#2#3#3#0#3#2",
+"#3#2#3#2#3#3#0#3#2#3#3#2#3#2#3#X#E#0#3#2#3#0#3#3#2#3#0#3#2#3#3#0",
+"#3#2#3#0#3#2#3#2#3#0#3#2#3#3#0#3#W#2#3#0#3#3#2#3#0#3#3#2#3#0#3#2",
+"#3#2#3#2#3#3#0#3#2#3#3#2#3#0#3#2#3#3#0#3#2#3#2#3#2#3#0#3#3#2#3#2"
+};
diff --git a/code/unix/run-target.sh b/code/unix/run-target.sh
new file mode 100755
index 0000000..7a5830c
--- /dev/null
+++ b/code/unix/run-target.sh
@@ -0,0 +1,12 @@
+#!/bin/sh
+# for easy use with Anjuta
+cd `dirname $0`/debugi386-glibc
+echo "in $PWD"
+
+# now execute whatever you want
+#./linuxquake3-smp +set fs_basepath /usr/local/games/quake3 +set developer 1 +set r_smp 1 +set r_showsmp 1 +devmap mythology
+
+#gvd ./linuxquake3-smp --pargs +set logfile 2 +set fs_basepath /usr/local/games/quake3 +set developer 1 +set r_smp 0 +set r_showsmp 1 +devmap mythology
+
+./linuxquake3-smp +set logfile 2 +set fs_basepath /usr/local/games/quake3 +set developer 1 +set r_smp 1 +set r_fullscreen 0 +set r_showsmp 1 +devmap mythology &
+gvd ./linuxquake3-smp
diff --git a/code/unix/snapvector.nasm b/code/unix/snapvector.nasm
new file mode 100755
index 0000000..3232fde
--- /dev/null
+++ b/code/unix/snapvector.nasm
@@ -0,0 +1,95 @@
+;===========================================================================
+;Copyright (C) 1999-2005 Id Software, Inc.
+;
+;This file is part of Quake III Arena source code.
+;
+;Quake III Arena source code is free software; you can redistribute it
+;and/or modify it under the terms of the GNU General Public License as
+;published by the Free Software Foundation; either version 2 of the License,
+;or (at your option) any later version.
+;
+;Quake III Arena source code is distributed in the hope that it will be
+;useful, but WITHOUT ANY WARRANTY; without even the implied warranty of
+;MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+;GNU General Public License for more details.
+;
+;You should have received a copy of the GNU General Public License
+;along with Foobar; if not, write to the Free Software
+;Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+;===========================================================================
+
+;
+; Sys_SnapVector NASM code (Andrew Henderson)
+; See win32/win_shared.c for the Win32 equivalent
+; This code is provided to ensure that the
+; rounding behavior (and, if necessary, the
+; precision) of DLL and QVM code are identical
+; e.g. for network-visible operations.
+; See ftol.nasm for operations on a single float,
+; as used in compiled VM and DLL code that does
+; not use this system trap.
+;
+
+
+segment .data
+
+fpucw dd 0
+cw037F dd 0x037F ; Rounding to nearest (even).
+
+segment .text
+
+; void Sys_SnapVector( float *v )
+global Sys_SnapVector
+Sys_SnapVector:
+ push eax
+ push ebp
+ mov ebp, esp
+
+ fnstcw [fpucw]
+ mov eax, dword [ebp + 12]
+ fldcw [cw037F]
+ fld dword [eax]
+ fistp dword [eax]
+ fild dword [eax]
+ fstp dword [eax]
+ fld dword [eax + 4]
+ fistp dword [eax + 4]
+ fild dword [eax + 4]
+ fstp dword [eax + 4]
+ fld dword [eax + 8]
+ fistp dword [eax + 8]
+ fild dword [eax + 8]
+ fstp dword [eax + 8]
+ fldcw [fpucw]
+
+ pop ebp
+ pop eax
+ ret
+
+; void Sys_SnapVectorCW( float *v, unsigned short int cw )
+global Sys_SnapVectorCW
+Sys_SnapVector_cw:
+ push eax
+ push ebp
+ mov ebp, esp
+
+ fnstcw [fpucw]
+ mov eax, dword [ebp + 12]
+ fldcw [ebp + 16]
+ fld dword [eax]
+ fistp dword [eax]
+ fild dword [eax]
+ fstp dword [eax]
+ fld dword [eax + 4]
+ fistp dword [eax + 4]
+ fild dword [eax + 4]
+ fstp dword [eax + 4]
+ fld dword [eax + 8]
+ fistp dword [eax + 8]
+ fild dword [eax + 8]
+ fstp dword [eax + 8]
+ fldcw [fpucw]
+
+ pop ebp
+ pop eax
+ ret \ No newline at end of file
diff --git a/code/unix/snd_mixa.s b/code/unix/snd_mixa.s
new file mode 100755
index 0000000..3fc35a8
--- /dev/null
+++ b/code/unix/snd_mixa.s
@@ -0,0 +1,217 @@
+/*
+===========================================================================
+Copyright (C) 1999-2005 Id Software, Inc.
+
+This file is part of Quake III Arena source code.
+
+Quake III Arena source code is free software; you can redistribute it
+and/or modify it under the terms of the GNU General Public License as
+published by the Free Software Foundation; either version 2 of the License,
+or (at your option) any later version.
+
+Quake III Arena source code is distributed in the hope that it will be
+useful, but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with Foobar; if not, write to the Free Software
+Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+===========================================================================
+*/
+//
+// snd_mixa.s
+// x86 assembly-language sound code
+//
+
+#include "qasm.h"
+
+#if id386
+
+ .text
+
+#if 0
+//----------------------------------------------------------------------
+// 8-bit sound-mixing code
+//----------------------------------------------------------------------
+
+#define ch 4+16
+#define sc 8+16
+#define count 12+16
+
+.globl C(S_PaintChannelFrom8)
+C(S_PaintChannelFrom8):
+ pushl %esi // preserve register variables
+ pushl %edi
+ pushl %ebx
+ pushl %ebp
+
+// int data;
+// short *lscale, *rscale;
+// unsigned char *sfx;
+// int i;
+
+ movl ch(%esp),%ebx
+ movl sc(%esp),%esi
+
+// if (ch->leftvol > 255)
+// ch->leftvol = 255;
+// if (ch->rightvol > 255)
+// ch->rightvol = 255;
+ movl ch_leftvol(%ebx),%eax
+ movl ch_rightvol(%ebx),%edx
+ cmpl $255,%eax
+ jna LLeftSet
+ movl $255,%eax
+LLeftSet:
+ cmpl $255,%edx
+ jna LRightSet
+ movl $255,%edx
+LRightSet:
+
+// lscale = snd_scaletable[ch->leftvol >> 3];
+// rscale = snd_scaletable[ch->rightvol >> 3];
+// sfx = (signed char *)sc->data + ch->pos;
+// ch->pos += count;
+ andl $0xF8,%eax
+ addl $20,%esi
+ movl (%esi),%esi
+ andl $0xF8,%edx
+ movl ch_pos(%ebx),%edi
+ movl count(%esp),%ecx
+ addl %edi,%esi
+ shll $7,%eax
+ addl %ecx,%edi
+ shll $7,%edx
+ movl %edi,ch_pos(%ebx)
+ addl $(C(snd_scaletable)),%eax
+ addl $(C(snd_scaletable)),%edx
+ subl %ebx,%ebx
+ movb -1(%esi,%ecx,1),%bl
+
+ testl $1,%ecx
+ jz LMix8Loop
+
+ movl (%eax,%ebx,4),%edi
+ movl (%edx,%ebx,4),%ebp
+ addl C(paintbuffer)+psp_left-psp_size(,%ecx,psp_size),%edi
+ addl C(paintbuffer)+psp_right-psp_size(,%ecx,psp_size),%ebp
+ movl %edi,C(paintbuffer)+psp_left-psp_size(,%ecx,psp_size)
+ movl %ebp,C(paintbuffer)+psp_right-psp_size(,%ecx,psp_size)
+ movb -2(%esi,%ecx,1),%bl
+
+ decl %ecx
+ jz LDone
+
+// for (i=0 ; i<count ; i++)
+// {
+LMix8Loop:
+
+// data = sfx[i];
+// paintbuffer[i].left += lscale[data];
+// paintbuffer[i].right += rscale[data];
+ movl (%eax,%ebx,4),%edi
+ movl (%edx,%ebx,4),%ebp
+ addl C(paintbuffer)+psp_left-psp_size(,%ecx,psp_size),%edi
+ addl C(paintbuffer)+psp_right-psp_size(,%ecx,psp_size),%ebp
+ movb -2(%esi,%ecx,1),%bl
+ movl %edi,C(paintbuffer)+psp_left-psp_size(,%ecx,psp_size)
+ movl %ebp,C(paintbuffer)+psp_right-psp_size(,%ecx,psp_size)
+
+ movl (%eax,%ebx,4),%edi
+ movl (%edx,%ebx,4),%ebp
+ movb -3(%esi,%ecx,1),%bl
+ addl C(paintbuffer)+psp_left-psp_size*2(,%ecx,psp_size),%edi
+ addl C(paintbuffer)+psp_right-psp_size*2(,%ecx,psp_size),%ebp
+ movl %edi,C(paintbuffer)+psp_left-psp_size*2(,%ecx,psp_size)
+ movl %ebp,C(paintbuffer)+psp_right-psp_size*2(,%ecx,psp_size)
+
+// }
+ subl $2,%ecx
+ jnz LMix8Loop
+
+LDone:
+ popl %ebp
+ popl %ebx
+ popl %edi
+ popl %esi
+
+ ret
+
+
+#endif
+
+//----------------------------------------------------------------------
+// Transfer of stereo buffer to 16-bit DMA buffer code
+//----------------------------------------------------------------------
+
+.globl C(S_WriteLinearBlastStereo16)
+C(S_WriteLinearBlastStereo16):
+ pushl %edi
+ pushl %ebx
+
+// int i;
+// int val;
+ movl C(snd_linear_count),%ecx
+ movl C(snd_p),%ebx
+ movl C(snd_out),%edi
+
+// for (i=0 ; i<snd_linear_count ; i+=2)
+// {
+LWLBLoopTop:
+
+// val = (snd_p[i]*snd_vol)>>8;
+// if (val > 0x7fff)
+// snd_out[i] = 0x7fff;
+// else if (val < (short)0x8000)
+// snd_out[i] = (short)0x8000;
+// else
+// snd_out[i] = val;
+ movl -8(%ebx,%ecx,4),%eax
+ sarl $8,%eax
+ cmpl $0x7FFF,%eax
+ jg LClampHigh
+ cmpl $0xFFFF8000,%eax
+ jnl LClampDone
+ movl $0xFFFF8000,%eax
+ jmp LClampDone
+LClampHigh:
+ movl $0x7FFF,%eax
+LClampDone:
+
+// val = (snd_p[i+1]*snd_vol)>>8;
+// if (val > 0x7fff)
+// snd_out[i+1] = 0x7fff;
+// else if (val < (short)0x8000)
+// snd_out[i+1] = (short)0x8000;
+// else
+// snd_out[i+1] = val;
+ movl -4(%ebx,%ecx,4),%edx
+ sarl $8,%edx
+ cmpl $0x7FFF,%edx
+ jg LClampHigh2
+ cmpl $0xFFFF8000,%edx
+ jnl LClampDone2
+ movl $0xFFFF8000,%edx
+ jmp LClampDone2
+LClampHigh2:
+ movl $0x7FFF,%edx
+LClampDone2:
+ shll $16,%edx
+ andl $0xFFFF,%eax
+ orl %eax,%edx
+ movl %edx,-4(%edi,%ecx,2)
+
+// }
+ subl $2,%ecx
+ jnz LWLBLoopTop
+
+// snd_p += snd_linear_count;
+
+ popl %ebx
+ popl %edi
+
+ ret
+
+#endif // id386
+
diff --git a/code/unix/sys_dosa.s b/code/unix/sys_dosa.s
new file mode 100755
index 0000000..4b1aca5
--- /dev/null
+++ b/code/unix/sys_dosa.s
@@ -0,0 +1,115 @@
+/*
+===========================================================================
+Copyright (C) 1999-2005 Id Software, Inc.
+
+This file is part of Quake III Arena source code.
+
+Quake III Arena source code is free software; you can redistribute it
+and/or modify it under the terms of the GNU General Public License as
+published by the Free Software Foundation; either version 2 of the License,
+or (at your option) any later version.
+
+Quake III Arena source code is distributed in the hope that it will be
+useful, but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with Foobar; if not, write to the Free Software
+Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+===========================================================================
+*/
+//
+// sys_dosa.s
+// x86 assembly-language DOS-dependent routines.
+
+#include "qasm.h"
+
+
+ .data
+
+ .align 4
+fpenv:
+ .long 0, 0, 0, 0, 0, 0, 0, 0
+
+ .text
+
+.globl C(MaskExceptions)
+C(MaskExceptions):
+ fnstenv fpenv
+ orl $0x3F,fpenv
+ fldenv fpenv
+
+ ret
+
+#if 0
+.globl C(unmaskexceptions)
+C(unmaskexceptions):
+ fnstenv fpenv
+ andl $0xFFFFFFE0,fpenv
+ fldenv fpenv
+
+ ret
+#endif
+
+ .data
+
+ .align 4
+.globl ceil_cw, single_cw, full_cw, cw, pushed_cw
+ceil_cw: .long 0
+single_cw: .long 0
+full_cw: .long 0
+cw: .long 0
+pushed_cw: .long 0
+
+ .text
+
+.globl C(Sys_LowFPPrecision)
+C(Sys_LowFPPrecision):
+ fldcw single_cw
+
+ ret
+
+.globl C(Sys_HighFPPrecision)
+C(Sys_HighFPPrecision):
+ fldcw full_cw
+
+ ret
+
+.globl C(Sys_PushFPCW_SetHigh)
+C(Sys_PushFPCW_SetHigh):
+ fnstcw pushed_cw
+ fldcw full_cw
+
+ ret
+
+.globl C(Sys_PopFPCW)
+C(Sys_PopFPCW):
+ fldcw pushed_cw
+
+ ret
+
+.globl C(Sys_SetFPCW)
+C(Sys_SetFPCW):
+ fnstcw cw
+ movl cw,%eax
+#if id386
+ andb $0xF0,%ah
+ orb $0x03,%ah // round mode, 64-bit precision
+#endif
+ movl %eax,full_cw
+
+#if id386
+ andb $0xF0,%ah
+ orb $0x0C,%ah // chop mode, single precision
+#endif
+ movl %eax,single_cw
+
+#if id386
+ andb $0xF0,%ah
+ orb $0x08,%ah // ceil mode, single precision
+#endif
+ movl %eax,ceil_cw
+
+ ret
+
diff --git a/code/unix/unix_glw.h b/code/unix/unix_glw.h
new file mode 100755
index 0000000..9a84f0c
--- /dev/null
+++ b/code/unix/unix_glw.h
@@ -0,0 +1,38 @@
+/*
+===========================================================================
+Copyright (C) 1999-2005 Id Software, Inc.
+
+This file is part of Quake III Arena source code.
+
+Quake III Arena source code is free software; you can redistribute it
+and/or modify it under the terms of the GNU General Public License as
+published by the Free Software Foundation; either version 2 of the License,
+or (at your option) any later version.
+
+Quake III Arena source code is distributed in the hope that it will be
+useful, but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with Foobar; if not, write to the Free Software
+Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+===========================================================================
+*/
+#if !( defined __linux__ || defined __FreeBSD__ )
+#error You should include this file only on Linux/FreeBSD platforms
+#endif
+
+#ifndef __GLW_LINUX_H__
+#define __GLW_LINUX_H__
+
+typedef struct
+{
+ void *OpenGLLib; // instance of OpenGL library
+
+ FILE *log_fp;
+} glwstate_t;
+
+extern glwstate_t glw_state;
+
+#endif
diff --git a/code/unix/unix_main.c b/code/unix/unix_main.c
new file mode 100755
index 0000000..e439568
--- /dev/null
+++ b/code/unix/unix_main.c
@@ -0,0 +1,1273 @@
+/*
+===========================================================================
+Copyright (C) 1999-2005 Id Software, Inc.
+
+This file is part of Quake III Arena source code.
+
+Quake III Arena source code is free software; you can redistribute it
+and/or modify it under the terms of the GNU General Public License as
+published by the Free Software Foundation; either version 2 of the License,
+or (at your option) any later version.
+
+Quake III Arena source code is distributed in the hope that it will be
+useful, but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with Foobar; if not, write to the Free Software
+Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+===========================================================================
+*/
+#include <unistd.h>
+#include <signal.h>
+#include <stdlib.h>
+#include <limits.h>
+#include <sys/time.h>
+#include <sys/types.h>
+#include <unistd.h>
+#include <fcntl.h>
+#include <stdarg.h>
+#include <stdio.h>
+#include <sys/ipc.h>
+#include <sys/shm.h>
+#include <sys/stat.h>
+#include <string.h>
+#include <ctype.h>
+#include <sys/wait.h>
+#include <sys/mman.h>
+#include <errno.h>
+#ifdef __linux__ // rb010123
+ #include <mntent.h>
+#endif
+#include <dlfcn.h>
+
+#ifdef __linux__
+ #include <fpu_control.h> // bk001213 - force dumps on divide by zero
+#endif
+
+// FIXME TTimo should we gard this? most *nix system should comply?
+#include <termios.h>
+
+#include "../game/q_shared.h"
+#include "../qcommon/qcommon.h"
+#include "../renderer/tr_public.h"
+
+#include "linux_local.h" // bk001204
+
+// Structure containing functions exported from refresh DLL
+refexport_t re;
+
+unsigned sys_frame_time;
+
+uid_t saved_euid;
+qboolean stdin_active = qtrue;
+
+// =============================================================
+// tty console variables
+// =============================================================
+
+// enable/disabled tty input mode
+// NOTE TTimo this is used during startup, cannot be changed during run
+static cvar_t *ttycon = NULL;
+// general flag to tell about tty console mode
+static qboolean ttycon_on = qfalse;
+// when printing general stuff to stdout stderr (Sys_Printf)
+// we need to disable the tty console stuff
+// this increments so we can recursively disable
+static int ttycon_hide = 0;
+// some key codes that the terminal may be using
+// TTimo NOTE: I'm not sure how relevant this is
+static int tty_erase;
+static int tty_eof;
+
+static struct termios tty_tc;
+
+static field_t tty_con;
+
+// history
+// NOTE TTimo this is a bit duplicate of the graphical console history
+// but it's safer and faster to write our own here
+#define TTY_HISTORY 32
+static field_t ttyEditLines[TTY_HISTORY];
+static int hist_current = -1, hist_count = 0;
+
+// =======================================================================
+// General routines
+// =======================================================================
+
+// bk001207
+#define MEM_THRESHOLD 96*1024*1024
+
+/*
+==================
+Sys_LowPhysicalMemory()
+==================
+*/
+qboolean Sys_LowPhysicalMemory() {
+ //MEMORYSTATUS stat;
+ //GlobalMemoryStatus (&stat);
+ //return (stat.dwTotalPhys <= MEM_THRESHOLD) ? qtrue : qfalse;
+ return qfalse; // bk001207 - FIXME
+}
+
+/*
+==================
+Sys_FunctionCmp
+==================
+*/
+int Sys_FunctionCmp(void *f1, void *f2) {
+ return qtrue;
+}
+
+/*
+==================
+Sys_FunctionCheckSum
+==================
+*/
+int Sys_FunctionCheckSum(void *f1) {
+ return 0;
+}
+
+/*
+==================
+Sys_MonkeyShouldBeSpanked
+==================
+*/
+int Sys_MonkeyShouldBeSpanked( void ) {
+ return 0;
+}
+
+void Sys_BeginProfiling( void ) {
+}
+
+/*
+=================
+Sys_In_Restart_f
+
+Restart the input subsystem
+=================
+*/
+void Sys_In_Restart_f( void )
+{
+ IN_Shutdown();
+ IN_Init();
+}
+
+// =============================================================
+// tty console routines
+// NOTE: if the user is editing a line when something gets printed to the early console then it won't look good
+// so we provide tty_Clear and tty_Show to be called before and after a stdout or stderr output
+// =============================================================
+
+// flush stdin, I suspect some terminals are sending a LOT of shit
+// FIXME TTimo relevant?
+void tty_FlushIn()
+{
+ char key;
+ while (read(0, &key, 1)!=-1);
+}
+
+// do a backspace
+// TTimo NOTE: it seems on some terminals just sending '\b' is not enough
+// so for now, in any case we send "\b \b" .. yeah well ..
+// (there may be a way to find out if '\b' alone would work though)
+void tty_Back()
+{
+ char key;
+ key = '\b';
+ write(1, &key, 1);
+ key = ' ';
+ write(1, &key, 1);
+ key = '\b';
+ write(1, &key, 1);
+}
+
+// clear the display of the line currently edited
+// bring cursor back to beginning of line
+void tty_Hide()
+{
+ int i;
+ assert(ttycon_on);
+ if (ttycon_hide)
+ {
+ ttycon_hide++;
+ return;
+ }
+ if (tty_con.cursor>0)
+ {
+ for (i=0; i<tty_con.cursor; i++)
+ {
+ tty_Back();
+ }
+ }
+ ttycon_hide++;
+}
+
+// show the current line
+// FIXME TTimo need to position the cursor if needed??
+void tty_Show()
+{
+ int i;
+ assert(ttycon_on);
+ assert(ttycon_hide>0);
+ ttycon_hide--;
+ if (ttycon_hide == 0)
+ {
+ if (tty_con.cursor)
+ {
+ for (i=0; i<tty_con.cursor; i++)
+ {
+ write(1, tty_con.buffer+i, 1);
+ }
+ }
+ }
+}
+
+// never exit without calling this, or your terminal will be left in a pretty bad state
+void Sys_ConsoleInputShutdown()
+{
+ if (ttycon_on)
+ {
+ Com_Printf("Shutdown tty console\n");
+ tcsetattr (0, TCSADRAIN, &tty_tc);
+ }
+}
+
+void Hist_Add(field_t *field)
+{
+ int i;
+ assert(hist_count <= TTY_HISTORY);
+ assert(hist_count >= 0);
+ assert(hist_current >= -1);
+ assert(hist_current <= hist_count);
+ // make some room
+ for (i=TTY_HISTORY-1; i>0; i--)
+ {
+ ttyEditLines[i] = ttyEditLines[i-1];
+ }
+ ttyEditLines[0] = *field;
+ if (hist_count<TTY_HISTORY)
+ {
+ hist_count++;
+ }
+ hist_current = -1; // re-init
+}
+
+field_t *Hist_Prev()
+{
+ int hist_prev;
+ assert(hist_count <= TTY_HISTORY);
+ assert(hist_count >= 0);
+ assert(hist_current >= -1);
+ assert(hist_current <= hist_count);
+ hist_prev = hist_current + 1;
+ if (hist_prev >= hist_count)
+ {
+ return NULL;
+ }
+ hist_current++;
+ return &(ttyEditLines[hist_current]);
+}
+
+field_t *Hist_Next()
+{
+ assert(hist_count <= TTY_HISTORY);
+ assert(hist_count >= 0);
+ assert(hist_current >= -1);
+ assert(hist_current <= hist_count);
+ if (hist_current >= 0)
+ {
+ hist_current--;
+ }
+ if (hist_current == -1)
+ {
+ return NULL;
+ }
+ return &(ttyEditLines[hist_current]);
+}
+
+// =============================================================
+// general sys routines
+// =============================================================
+
+#if 0
+// NOTE TTimo this is not used .. looks interesting though? protection against buffer overflow kind of stuff?
+void Sys_Printf (char *fmt, ...)
+{
+ va_list argptr;
+ char text[1024];
+ unsigned char *p;
+
+ va_start (argptr,fmt);
+ vsprintf (text,fmt,argptr);
+ va_end (argptr);
+
+ if (strlen(text) > sizeof(text))
+ Sys_Error("memory overwrite in Sys_Printf");
+
+ for (p = (unsigned char *)text; *p; p++)
+ {
+ *p &= 0x7f;
+ if ((*p > 128 || *p < 32) && *p != 10 && *p != 13 && *p != 9)
+ printf("[%02x]", *p);
+ else
+ putc(*p, stdout);
+ }
+}
+#endif
+
+// single exit point (regular exit or in case of signal fault)
+void Sys_Exit( int ex ) {
+ Sys_ConsoleInputShutdown();
+
+#ifdef NDEBUG // regular behavior
+
+ // We can't do this
+ // as long as GL DLL's keep installing with atexit...
+ //exit(ex);
+ _exit(ex);
+#else
+
+ // Give me a backtrace on error exits.
+ assert( ex == 0 );
+ exit(ex);
+#endif
+}
+
+
+void Sys_Quit (void) {
+ CL_Shutdown ();
+ fcntl (0, F_SETFL, fcntl (0, F_GETFL, 0) & ~FNDELAY);
+ Sys_Exit(0);
+}
+
+void Sys_Init(void)
+{
+ Cmd_AddCommand ("in_restart", Sys_In_Restart_f);
+
+#if defined __linux__
+#if defined __i386__
+ Cvar_Set( "arch", "linux i386" );
+#elif defined __alpha__
+ Cvar_Set( "arch", "linux alpha" );
+#elif defined __sparc__
+ Cvar_Set( "arch", "linux sparc" );
+#elif defined __FreeBSD__
+
+#if defined __i386__ // FreeBSD
+ Cvar_Set( "arch", "freebsd i386" );
+#elif defined __alpha__
+ Cvar_Set( "arch", "freebsd alpha" );
+#else
+ Cvar_Set( "arch", "freebsd unknown" );
+#endif // FreeBSD
+
+#else
+ Cvar_Set( "arch", "linux unknown" );
+#endif
+#elif defined __sun__
+#if defined __i386__
+ Cvar_Set( "arch", "solaris x86" );
+#elif defined __sparc__
+ Cvar_Set( "arch", "solaris sparc" );
+#else
+ Cvar_Set( "arch", "solaris unknown" );
+#endif
+#elif defined __sgi__
+#if defined __mips__
+ Cvar_Set( "arch", "sgi mips" );
+#else
+ Cvar_Set( "arch", "sgi unknown" );
+#endif
+#else
+ Cvar_Set( "arch", "unknown" );
+#endif
+
+ Cvar_Set( "username", Sys_GetCurrentUser() );
+
+ IN_Init();
+
+}
+
+void Sys_Error( const char *error, ...)
+{
+ va_list argptr;
+ char string[1024];
+
+ // change stdin to non blocking
+ // NOTE TTimo not sure how well that goes with tty console mode
+ fcntl (0, F_SETFL, fcntl (0, F_GETFL, 0) & ~FNDELAY);
+
+ // don't bother do a show on this one heh
+ if (ttycon_on)
+ {
+ tty_Hide();
+ }
+
+ CL_Shutdown ();
+
+ va_start (argptr,error);
+ vsprintf (string,error,argptr);
+ va_end (argptr);
+ fprintf(stderr, "Sys_Error: %s\n", string);
+
+ Sys_Exit( 1 ); // bk010104 - use single exit point.
+}
+
+void Sys_Warn (char *warning, ...)
+{
+ va_list argptr;
+ char string[1024];
+
+ va_start (argptr,warning);
+ vsprintf (string,warning,argptr);
+ va_end (argptr);
+
+ if (ttycon_on)
+ {
+ tty_Hide();
+ }
+
+ fprintf(stderr, "Warning: %s", string);
+
+ if (ttycon_on)
+ {
+ tty_Show();
+ }
+}
+
+/*
+============
+Sys_FileTime
+
+returns -1 if not present
+============
+*/
+int Sys_FileTime (char *path)
+{
+ struct stat buf;
+
+ if (stat (path,&buf) == -1)
+ return -1;
+
+ return buf.st_mtime;
+}
+
+void floating_point_exception_handler(int whatever)
+{
+ signal(SIGFPE, floating_point_exception_handler);
+}
+
+// initialize the console input (tty mode if wanted and possible)
+void Sys_ConsoleInputInit()
+{
+ struct termios tc;
+
+ // TTimo
+ // https://zerowing.idsoftware.com/bugzilla/show_bug.cgi?id=390
+ // ttycon 0 or 1, if the process is backgrounded (running non interactively)
+ // then SIGTTIN or SIGTOU is emitted, if not catched, turns into a SIGSTP
+ signal(SIGTTIN, SIG_IGN);
+ signal(SIGTTOU, SIG_IGN);
+
+ // FIXME TTimo initialize this in Sys_Init or something?
+ ttycon = Cvar_Get("ttycon", "1", 0);
+ if (ttycon && ttycon->value)
+ {
+ if (isatty(STDIN_FILENO)!=1)
+ {
+ Com_Printf("stdin is not a tty, tty console mode failed\n");
+ Cvar_Set("ttycon", "0");
+ ttycon_on = qfalse;
+ return;
+ }
+ Com_Printf("Started tty console (use +set ttycon 0 to disable)\n");
+ Field_Clear(&tty_con);
+ tcgetattr (0, &tty_tc);
+ tty_erase = tty_tc.c_cc[VERASE];
+ tty_eof = tty_tc.c_cc[VEOF];
+ tc = tty_tc;
+ /*
+ ECHO: don't echo input characters
+ ICANON: enable canonical mode. This enables the special
+ characters EOF, EOL, EOL2, ERASE, KILL, REPRINT,
+ STATUS, and WERASE, and buffers by lines.
+ ISIG: when any of the characters INTR, QUIT, SUSP, or
+ DSUSP are received, generate the corresponding sig­
+ nal
+ */
+ tc.c_lflag &= ~(ECHO | ICANON);
+ /*
+ ISTRIP strip off bit 8
+ INPCK enable input parity checking
+ */
+ tc.c_iflag &= ~(ISTRIP | INPCK);
+ tc.c_cc[VMIN] = 1;
+ tc.c_cc[VTIME] = 0;
+ tcsetattr (0, TCSADRAIN, &tc);
+ ttycon_on = qtrue;
+ } else
+ ttycon_on = qfalse;
+}
+
+char *Sys_ConsoleInput(void)
+{
+ // we use this when sending back commands
+ static char text[256];
+ int i;
+ int avail;
+ char key;
+ field_t *history;
+
+ if (ttycon && ttycon->value)
+ {
+ avail = read(0, &key, 1);
+ if (avail != -1)
+ {
+ // we have something
+ // backspace?
+ // NOTE TTimo testing a lot of values .. seems it's the only way to get it to work everywhere
+ if ((key == tty_erase) || (key == 127) || (key == 8))
+ {
+ if (tty_con.cursor > 0)
+ {
+ tty_con.cursor--;
+ tty_con.buffer[tty_con.cursor] = '\0';
+ tty_Back();
+ }
+ return NULL;
+ }
+ // check if this is a control char
+ if ((key) && (key) < ' ')
+ {
+ if (key == '\n')
+ {
+ // push it in history
+ Hist_Add(&tty_con);
+ strcpy(text, tty_con.buffer);
+ Field_Clear(&tty_con);
+ key = '\n';
+ write(1, &key, 1);
+ return text;
+ }
+ if (key == '\t')
+ {
+ tty_Hide();
+ Field_CompleteCommand( &tty_con );
+ // Field_CompleteCommand does weird things to the string, do a cleanup
+ // it adds a '\' at the beginning of the string
+ // cursor doesn't reflect actual length of the string that's sent back
+ tty_con.cursor = strlen(tty_con.buffer);
+ if (tty_con.cursor>0)
+ {
+ if (tty_con.buffer[0] == '\\')
+ {
+ for (i=0; i<=tty_con.cursor; i++)
+ {
+ tty_con.buffer[i] = tty_con.buffer[i+1];
+ }
+ tty_con.cursor--;
+ }
+ }
+ tty_Show();
+ return NULL;
+ }
+ avail = read(0, &key, 1);
+ if (avail != -1)
+ {
+ // VT 100 keys
+ if (key == '[' || key == 'O')
+ {
+ avail = read(0, &key, 1);
+ if (avail != -1)
+ {
+ switch (key)
+ {
+ case 'A':
+ history = Hist_Prev();
+ if (history)
+ {
+ tty_Hide();
+ tty_con = *history;
+ tty_Show();
+ }
+ tty_FlushIn();
+ return NULL;
+ break;
+ case 'B':
+ history = Hist_Next();
+ tty_Hide();
+ if (history)
+ {
+ tty_con = *history;
+ } else
+ {
+ Field_Clear(&tty_con);
+ }
+ tty_Show();
+ tty_FlushIn();
+ return NULL;
+ break;
+ case 'C':
+ return NULL;
+ case 'D':
+ return NULL;
+ }
+ }
+ }
+ }
+ Com_DPrintf("droping ISCTL sequence: %d, tty_erase: %d\n", key, tty_erase);
+ tty_FlushIn();
+ return NULL;
+ }
+ // push regular character
+ tty_con.buffer[tty_con.cursor] = key;
+ tty_con.cursor++;
+ // print the current line (this is differential)
+ write(1, &key, 1);
+ }
+ return NULL;
+ } else
+ {
+ int len;
+ fd_set fdset;
+ struct timeval timeout;
+
+ if (!com_dedicated || !com_dedicated->value)
+ return NULL;
+
+ if (!stdin_active)
+ return NULL;
+
+ FD_ZERO(&fdset);
+ FD_SET(0, &fdset); // stdin
+ timeout.tv_sec = 0;
+ timeout.tv_usec = 0;
+ if (select (1, &fdset, NULL, NULL, &timeout) == -1 || !FD_ISSET(0, &fdset))
+ {
+ return NULL;
+ }
+
+ len = read (0, text, sizeof(text));
+ if (len == 0)
+ { // eof!
+ stdin_active = qfalse;
+ return NULL;
+ }
+
+ if (len < 1)
+ return NULL;
+ text[len-1] = 0; // rip off the /n and terminate
+
+ return text;
+ }
+}
+
+/*****************************************************************************/
+
+/*
+=================
+Sys_UnloadDll
+
+=================
+*/
+void Sys_UnloadDll( void *dllHandle ) {
+ // bk001206 - verbose error reporting
+ const char* err; // rb010123 - now const
+ if ( !dllHandle )
+ {
+ Com_Printf("Sys_UnloadDll(NULL)\n");
+ return;
+ }
+ dlclose( dllHandle );
+ err = dlerror();
+ if ( err != NULL )
+ Com_Printf ( "Sys_UnloadGame failed on dlclose: \"%s\"!\n", err );
+}
+
+
+/*
+=================
+Sys_LoadDll
+
+Used to load a development dll instead of a virtual machine
+TTimo:
+changed the load procedure to match VFS logic, and allow developer use
+#1 look down current path
+#2 look in fs_homepath
+#3 look in fs_basepath
+=================
+*/
+extern char *FS_BuildOSPath( const char *base, const char *game, const char *qpath );
+
+void *Sys_LoadDll( const char *name, char *fqpath ,
+ int (**entryPoint)(int, ...),
+ int (*systemcalls)(int, ...) )
+{
+ void *libHandle;
+ void (*dllEntry)( int (*syscallptr)(int, ...) );
+ char curpath[MAX_OSPATH];
+ char fname[MAX_OSPATH];
+ char *basepath;
+ char *homepath;
+ char *pwdpath;
+ char *gamedir;
+ char *fn;
+ const char* err = NULL;
+
+ *fqpath = 0;
+
+ // bk001206 - let's have some paranoia
+ assert( name );
+
+ getcwd(curpath, sizeof(curpath));
+#if defined __i386__
+ snprintf (fname, sizeof(fname), "%si386.so", name);
+#elif defined __powerpc__ //rcg010207 - PPC support.
+ snprintf (fname, sizeof(fname), "%sppc.so", name);
+#elif defined __axp__
+ snprintf (fname, sizeof(fname), "%saxp.so", name);
+#elif defined __mips__
+ snprintf (fname, sizeof(fname), "%smips.so", name);
+#else
+#error Unknown arch
+#endif
+
+// bk001129 - was RTLD_LAZY
+#define Q_RTLD RTLD_NOW
+
+ pwdpath = Sys_Cwd();
+ basepath = Cvar_VariableString( "fs_basepath" );
+ homepath = Cvar_VariableString( "fs_homepath" );
+ gamedir = Cvar_VariableString( "fs_game" );
+
+ // pwdpath
+ fn = FS_BuildOSPath( pwdpath, gamedir, fname );
+ Com_Printf( "Sys_LoadDll(%s)... \n", fn );
+ libHandle = dlopen( fn, Q_RTLD );
+
+ if ( !libHandle )
+ {
+ Com_Printf( "Sys_LoadDll(%s) failed:\n\"%s\"\n", fn, dlerror() );
+ // fs_homepath
+ fn = FS_BuildOSPath( homepath, gamedir, fname );
+ Com_Printf( "Sys_LoadDll(%s)... \n", fn );
+ libHandle = dlopen( fn, Q_RTLD );
+
+ if ( !libHandle )
+ {
+ Com_Printf( "Sys_LoadDll(%s) failed:\n\"%s\"\n", fn, dlerror() );
+ // fs_basepath
+ fn = FS_BuildOSPath( basepath, gamedir, fname );
+ Com_Printf( "Sys_LoadDll(%s)... \n", fn );
+ libHandle = dlopen( fn, Q_RTLD );
+
+ if ( !libHandle )
+ {
+#ifndef NDEBUG // bk001206 - in debug abort on failure
+ Com_Error ( ERR_FATAL, "Sys_LoadDll(%s) failed dlopen() completely!\n", name );
+#else
+ Com_Printf ( "Sys_LoadDll(%s) failed dlopen() completely!\n", name );
+#endif
+ return NULL;
+ } else
+ Com_Printf ( "Sys_LoadDll(%s): succeeded ...\n", fn );
+ } else
+ Com_Printf ( "Sys_LoadDll(%s): succeeded ...\n", fn );
+ } else
+ Com_Printf ( "Sys_LoadDll(%s): succeeded ...\n", fn );
+
+ dllEntry = dlsym( libHandle, "dllEntry" );
+ *entryPoint = dlsym( libHandle, "vmMain" );
+ if ( !*entryPoint || !dllEntry )
+ {
+ err = dlerror();
+#ifndef NDEBUG // bk001206 - in debug abort on failure
+ Com_Error ( ERR_FATAL, "Sys_LoadDll(%s) failed dlsym(vmMain):\n\"%s\" !\n", name, err );
+#else
+ Com_Printf ( "Sys_LoadDll(%s) failed dlsym(vmMain):\n\"%s\" !\n", name, err );
+#endif
+ dlclose( libHandle );
+ err = dlerror();
+ if ( err != NULL )
+ Com_Printf ( "Sys_LoadDll(%s) failed dlcose:\n\"%s\"\n", name, err );
+ return NULL;
+ }
+ Com_Printf ( "Sys_LoadDll(%s) found **vmMain** at %p \n", name, *entryPoint ); // bk001212
+ dllEntry( systemcalls );
+ Com_Printf ( "Sys_LoadDll(%s) succeeded!\n", name );
+ if ( libHandle ) Q_strncpyz ( fqpath , fn , MAX_QPATH ) ; // added 7/20/02 by T.Ray
+ return libHandle;
+}
+
+/*
+========================================================================
+
+BACKGROUND FILE STREAMING
+
+========================================================================
+*/
+
+#if 1
+
+void Sys_InitStreamThread( void ) {
+}
+
+void Sys_ShutdownStreamThread( void ) {
+}
+
+void Sys_BeginStreamedFile( fileHandle_t f, int readAhead ) {
+}
+
+void Sys_EndStreamedFile( fileHandle_t f ) {
+}
+
+int Sys_StreamedRead( void *buffer, int size, int count, fileHandle_t f ) {
+ return FS_Read( buffer, size * count, f );
+}
+
+void Sys_StreamSeek( fileHandle_t f, int offset, int origin ) {
+ FS_Seek( f, offset, origin );
+}
+
+#else
+
+typedef struct
+{
+ fileHandle_t file;
+ byte *buffer;
+ qboolean eof;
+ int bufferSize;
+ int streamPosition; // next byte to be returned by Sys_StreamRead
+ int threadPosition; // next byte to be read from file
+} streamState_t;
+
+streamState_t stream;
+
+/*
+===============
+Sys_StreamThread
+
+A thread will be sitting in this loop forever
+================
+*/
+void Sys_StreamThread( void )
+{
+ int buffer;
+ int count;
+ int readCount;
+ int bufferPoint;
+ int r;
+
+ // if there is any space left in the buffer, fill it up
+ if ( !stream.eof )
+ {
+ count = stream.bufferSize - (stream.threadPosition - stream.streamPosition);
+ if ( count )
+ {
+ bufferPoint = stream.threadPosition % stream.bufferSize;
+ buffer = stream.bufferSize - bufferPoint;
+ readCount = buffer < count ? buffer : count;
+ r = FS_Read ( stream.buffer + bufferPoint, readCount, stream.file );
+ stream.threadPosition += r;
+
+ if ( r != readCount )
+ stream.eof = qtrue;
+ }
+ }
+}
+
+/*
+===============
+Sys_InitStreamThread
+
+================
+*/
+void Sys_InitStreamThread( void )
+{
+}
+
+/*
+===============
+Sys_ShutdownStreamThread
+
+================
+*/
+void Sys_ShutdownStreamThread( void )
+{
+}
+
+
+/*
+===============
+Sys_BeginStreamedFile
+
+================
+*/
+void Sys_BeginStreamedFile( fileHandle_t f, int readAhead )
+{
+ if ( stream.file )
+ {
+ Com_Error( ERR_FATAL, "Sys_BeginStreamedFile: unclosed stream");
+ }
+
+ stream.file = f;
+ stream.buffer = Z_Malloc( readAhead );
+ stream.bufferSize = readAhead;
+ stream.streamPosition = 0;
+ stream.threadPosition = 0;
+ stream.eof = qfalse;
+}
+
+/*
+===============
+Sys_EndStreamedFile
+
+================
+*/
+void Sys_EndStreamedFile( fileHandle_t f )
+{
+ if ( f != stream.file )
+ {
+ Com_Error( ERR_FATAL, "Sys_EndStreamedFile: wrong file");
+ }
+
+ stream.file = 0;
+ Z_Free( stream.buffer );
+}
+
+
+/*
+===============
+Sys_StreamedRead
+
+================
+*/
+int Sys_StreamedRead( void *buffer, int size, int count, fileHandle_t f )
+{
+ int available;
+ int remaining;
+ int sleepCount;
+ int copy;
+ int bufferCount;
+ int bufferPoint;
+ byte *dest;
+
+ dest = (byte *)buffer;
+ remaining = size * count;
+
+ if ( remaining <= 0 )
+ {
+ Com_Error( ERR_FATAL, "Streamed read with non-positive size" );
+ }
+
+ sleepCount = 0;
+ while ( remaining > 0 )
+ {
+ available = stream.threadPosition - stream.streamPosition;
+ if ( !available )
+ {
+ if (stream.eof)
+ break;
+ Sys_StreamThread();
+ continue;
+ }
+
+ bufferPoint = stream.streamPosition % stream.bufferSize;
+ bufferCount = stream.bufferSize - bufferPoint;
+
+ copy = available < bufferCount ? available : bufferCount;
+ if ( copy > remaining )
+ {
+ copy = remaining;
+ }
+ memcpy( dest, stream.buffer + bufferPoint, copy );
+ stream.streamPosition += copy;
+ dest += copy;
+ remaining -= copy;
+ }
+
+ return(count * size - remaining) / size;
+}
+
+/*
+===============
+Sys_StreamSeek
+
+================
+*/
+void Sys_StreamSeek( fileHandle_t f, int offset, int origin ) {
+ // clear to that point
+ FS_Seek( f, offset, origin );
+ stream.streamPosition = 0;
+ stream.threadPosition = 0;
+ stream.eof = qfalse;
+}
+
+#endif
+
+/*
+========================================================================
+
+EVENT LOOP
+
+========================================================================
+*/
+
+// bk000306: upped this from 64
+#define MAX_QUED_EVENTS 256
+#define MASK_QUED_EVENTS ( MAX_QUED_EVENTS - 1 )
+
+sysEvent_t eventQue[MAX_QUED_EVENTS];
+// bk000306: initialize
+int eventHead = 0;
+int eventTail = 0;
+byte sys_packetReceived[MAX_MSGLEN];
+
+/*
+================
+Sys_QueEvent
+
+A time of 0 will get the current time
+Ptr should either be null, or point to a block of data that can
+be freed by the game later.
+================
+*/
+void Sys_QueEvent( int time, sysEventType_t type, int value, int value2, int ptrLength, void *ptr ) {
+ sysEvent_t *ev;
+
+ ev = &eventQue[ eventHead & MASK_QUED_EVENTS ];
+
+ // bk000305 - was missing
+ if ( eventHead - eventTail >= MAX_QUED_EVENTS )
+ {
+ Com_Printf("Sys_QueEvent: overflow\n");
+ // we are discarding an event, but don't leak memory
+ if ( ev->evPtr )
+ {
+ Z_Free( ev->evPtr );
+ }
+ eventTail++;
+ }
+
+ eventHead++;
+
+ if ( time == 0 )
+ {
+ time = Sys_Milliseconds();
+ }
+
+ ev->evTime = time;
+ ev->evType = type;
+ ev->evValue = value;
+ ev->evValue2 = value2;
+ ev->evPtrLength = ptrLength;
+ ev->evPtr = ptr;
+}
+
+/*
+================
+Sys_GetEvent
+
+================
+*/
+sysEvent_t Sys_GetEvent( void ) {
+ sysEvent_t ev;
+ char *s;
+ msg_t netmsg;
+ netadr_t adr;
+
+ // return if we have data
+ if ( eventHead > eventTail )
+ {
+ eventTail++;
+ return eventQue[ ( eventTail - 1 ) & MASK_QUED_EVENTS ];
+ }
+
+ // pump the message loop
+ // in vga this calls KBD_Update, under X, it calls GetEvent
+ Sys_SendKeyEvents ();
+
+ // check for console commands
+ s = Sys_ConsoleInput();
+ if ( s )
+ {
+ char *b;
+ int len;
+
+ len = strlen( s ) + 1;
+ b = Z_Malloc( len );
+ strcpy( b, s );
+ Sys_QueEvent( 0, SE_CONSOLE, 0, 0, len, b );
+ }
+
+ // check for other input devices
+ IN_Frame();
+
+ // check for network packets
+ MSG_Init( &netmsg, sys_packetReceived, sizeof( sys_packetReceived ) );
+ if ( Sys_GetPacket ( &adr, &netmsg ) )
+ {
+ netadr_t *buf;
+ int len;
+
+ // copy out to a seperate buffer for qeueing
+ len = sizeof( netadr_t ) + netmsg.cursize;
+ buf = Z_Malloc( len );
+ *buf = adr;
+ memcpy( buf+1, netmsg.data, netmsg.cursize );
+ Sys_QueEvent( 0, SE_PACKET, 0, 0, len, buf );
+ }
+
+ // return if we have data
+ if ( eventHead > eventTail )
+ {
+ eventTail++;
+ return eventQue[ ( eventTail - 1 ) & MASK_QUED_EVENTS ];
+ }
+
+ // create an empty event to return
+
+ memset( &ev, 0, sizeof( ev ) );
+ ev.evTime = Sys_Milliseconds();
+
+ return ev;
+}
+
+/*****************************************************************************/
+
+qboolean Sys_CheckCD( void ) {
+ return qtrue;
+}
+
+void Sys_AppActivate (void)
+{
+}
+
+char *Sys_GetClipboardData(void)
+{
+ return NULL;
+}
+
+void Sys_Print( const char *msg )
+{
+ if (ttycon_on)
+ {
+ tty_Hide();
+ }
+ fputs(msg, stderr);
+ if (ttycon_on)
+ {
+ tty_Show();
+ }
+}
+
+
+void Sys_ConfigureFPU() { // bk001213 - divide by zero
+#ifdef __linux__
+#ifdef __i386
+#ifndef NDEBUG
+
+ // bk0101022 - enable FPE's in debug mode
+ static int fpu_word = _FPU_DEFAULT & ~(_FPU_MASK_ZM | _FPU_MASK_IM);
+ int current = 0;
+ _FPU_GETCW(current);
+ if ( current!=fpu_word)
+ {
+#if 0
+ Com_Printf("FPU Control 0x%x (was 0x%x)\n", fpu_word, current );
+ _FPU_SETCW( fpu_word );
+ _FPU_GETCW( current );
+ assert(fpu_word==current);
+#endif
+ }
+#else // NDEBUG
+ static int fpu_word = _FPU_DEFAULT;
+ _FPU_SETCW( fpu_word );
+#endif // NDEBUG
+#endif // __i386
+#endif // __linux
+}
+
+void Sys_PrintBinVersion( const char* name ) {
+ char* date = __DATE__;
+ char* time = __TIME__;
+ char* sep = "==============================================================";
+ fprintf( stdout, "\n\n%s\n", sep );
+#ifdef DEDICATED
+ fprintf( stdout, "Linux Quake3 Dedicated Server [%s %s]\n", date, time );
+#else
+ fprintf( stdout, "Linux Quake3 Full Executable [%s %s]\n", date, time );
+#endif
+ fprintf( stdout, " local install: %s\n", name );
+ fprintf( stdout, "%s\n\n", sep );
+}
+
+void Sys_ParseArgs( int argc, char* argv[] ) {
+
+ if ( argc==2 )
+ {
+ if ( (!strcmp( argv[1], "--version" ))
+ || ( !strcmp( argv[1], "-v" )) )
+ {
+ Sys_PrintBinVersion( argv[0] );
+ Sys_Exit(0);
+ }
+ }
+}
+
+#include "../client/client.h"
+extern clientStatic_t cls;
+
+int main ( int argc, char* argv[] )
+{
+ // int oldtime, newtime; // bk001204 - unused
+ int len, i;
+ char *cmdline;
+ void Sys_SetDefaultCDPath(const char *path);
+
+ // go back to real user for config loads
+ saved_euid = geteuid();
+ seteuid(getuid());
+
+ Sys_ParseArgs( argc, argv ); // bk010104 - added this for support
+
+ Sys_SetDefaultCDPath(argv[0]);
+
+ // merge the command line, this is kinda silly
+ for (len = 1, i = 1; i < argc; i++)
+ len += strlen(argv[i]) + 1;
+ cmdline = malloc(len);
+ *cmdline = 0;
+ for (i = 1; i < argc; i++)
+ {
+ if (i > 1)
+ strcat(cmdline, " ");
+ strcat(cmdline, argv[i]);
+ }
+
+ // bk000306 - clear queues
+ memset( &eventQue[0], 0, MAX_QUED_EVENTS*sizeof(sysEvent_t) );
+ memset( &sys_packetReceived[0], 0, MAX_MSGLEN*sizeof(byte) );
+
+ Com_Init(cmdline);
+ NET_Init();
+
+ Sys_ConsoleInputInit();
+
+ fcntl(0, F_SETFL, fcntl (0, F_GETFL, 0) | FNDELAY);
+
+#ifdef DEDICATED
+ // init here for dedicated, as we don't have GLimp_Init
+ InitSig();
+#endif
+
+ while (1)
+ {
+#ifdef __linux__
+ Sys_ConfigureFPU();
+#endif
+ Com_Frame ();
+ }
+}
diff --git a/code/unix/unix_net.c b/code/unix/unix_net.c
new file mode 100755
index 0000000..299881f
--- /dev/null
+++ b/code/unix/unix_net.c
@@ -0,0 +1,616 @@
+/*
+===========================================================================
+Copyright (C) 1999-2005 Id Software, Inc.
+
+This file is part of Quake III Arena source code.
+
+Quake III Arena source code is free software; you can redistribute it
+and/or modify it under the terms of the GNU General Public License as
+published by the Free Software Foundation; either version 2 of the License,
+or (at your option) any later version.
+
+Quake III Arena source code is distributed in the hope that it will be
+useful, but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with Foobar; if not, write to the Free Software
+Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+===========================================================================
+*/
+// unix_net.c
+
+#include "../game/q_shared.h"
+#include "../qcommon/qcommon.h"
+
+#include <unistd.h>
+#include <sys/socket.h>
+#include <sys/time.h>
+#include <netinet/in.h>
+#include <netdb.h>
+#include <arpa/inet.h> // bk001204
+
+#include <sys/param.h>
+#include <sys/ioctl.h>
+#include <sys/uio.h>
+#include <errno.h>
+
+#ifdef MACOS_X
+#import <sys/sockio.h>
+#import <net/if.h>
+#import <net/if_types.h>
+
+#import <arpa/inet.h> // for inet_ntoa()
+#import <net/if_dl.h> // for 'struct sockaddr_dl'
+#endif
+
+static cvar_t *noudp;
+
+netadr_t net_local_adr;
+
+int ip_socket;
+int ipx_socket;
+
+#define MAX_IPS 16
+static int numIP;
+static byte localIP[MAX_IPS][4];
+
+int NET_Socket (char *net_interface, int port);
+char *NET_ErrorString (void);
+
+//=============================================================================
+
+void NetadrToSockadr (netadr_t *a, struct sockaddr_in *s)
+{
+ memset (s, 0, sizeof(*s));
+
+ if (a->type == NA_BROADCAST)
+ {
+ s->sin_family = AF_INET;
+
+ s->sin_port = a->port;
+ *(int *)&s->sin_addr = -1;
+ }
+ else if (a->type == NA_IP)
+ {
+ s->sin_family = AF_INET;
+
+ *(int *)&s->sin_addr = *(int *)&a->ip;
+ s->sin_port = a->port;
+ }
+}
+
+void SockadrToNetadr (struct sockaddr_in *s, netadr_t *a)
+{
+ *(int *)&a->ip = *(int *)&s->sin_addr;
+ a->port = s->sin_port;
+ a->type = NA_IP;
+}
+
+char *NET_BaseAdrToString (netadr_t a)
+{
+ static char s[64];
+
+ Com_sprintf (s, sizeof(s), "%i.%i.%i.%i", a.ip[0], a.ip[1], a.ip[2], a.ip[3]);
+
+ return s;
+}
+
+/*
+=============
+Sys_StringToAdr
+
+idnewt
+192.246.40.70
+=============
+*/
+qboolean Sys_StringToSockaddr (const char *s, struct sockaddr *sadr)
+{
+ struct hostent *h;
+ //char *colon; // bk001204 - unused
+
+ memset (sadr, 0, sizeof(*sadr));
+ ((struct sockaddr_in *)sadr)->sin_family = AF_INET;
+
+ ((struct sockaddr_in *)sadr)->sin_port = 0;
+
+ if ( s[0] >= '0' && s[0] <= '9')
+ {
+ *(int *)&((struct sockaddr_in *)sadr)->sin_addr = inet_addr(s);
+ }
+ else
+ {
+ if (! (h = gethostbyname(s)) )
+ return qfalse;
+ *(int *)&((struct sockaddr_in *)sadr)->sin_addr = *(int *)h->h_addr_list[0];
+ }
+
+ return qtrue;
+}
+
+/*
+=============
+Sys_StringToAdr
+
+localhost
+idnewt
+idnewt:28000
+192.246.40.70
+192.246.40.70:28000
+=============
+*/
+qboolean Sys_StringToAdr (const char *s, netadr_t *a)
+{
+ struct sockaddr_in sadr;
+
+ if (!Sys_StringToSockaddr (s, (struct sockaddr *)&sadr))
+ return qfalse;
+
+ SockadrToNetadr (&sadr, a);
+
+ return qtrue;
+}
+
+
+//=============================================================================
+
+qboolean Sys_GetPacket (netadr_t *net_from, msg_t *net_message)
+{
+ int ret;
+ struct sockaddr_in from;
+ int fromlen;
+ int net_socket;
+ int protocol;
+ int err;
+
+ for (protocol = 0 ; protocol < 2 ; protocol++)
+ {
+ if (protocol == 0)
+ net_socket = ip_socket;
+ else
+ net_socket = ipx_socket;
+
+ if (!net_socket)
+ continue;
+
+ fromlen = sizeof(from);
+ ret = recvfrom (net_socket, net_message->data, net_message->maxsize
+ , 0, (struct sockaddr *)&from, &fromlen);
+
+ SockadrToNetadr (&from, net_from);
+ // bk000305: was missing
+ net_message->readcount = 0;
+
+ if (ret == -1)
+ {
+ err = errno;
+
+ if (err == EWOULDBLOCK || err == ECONNREFUSED)
+ continue;
+ Com_Printf ("NET_GetPacket: %s from %s\n", NET_ErrorString(),
+ NET_AdrToString(*net_from));
+ continue;
+ }
+
+ if (ret == net_message->maxsize)
+ {
+ Com_Printf ("Oversize packet from %s\n", NET_AdrToString (*net_from));
+ continue;
+ }
+
+ net_message->cursize = ret;
+ return qtrue;
+ }
+
+ return qfalse;
+}
+
+//=============================================================================
+
+void Sys_SendPacket( int length, const void *data, netadr_t to )
+{
+ int ret;
+ struct sockaddr_in addr;
+ int net_socket;
+
+ if (to.type == NA_BROADCAST)
+ {
+ net_socket = ip_socket;
+ }
+ else if (to.type == NA_IP)
+ {
+ net_socket = ip_socket;
+ }
+ else if (to.type == NA_IPX)
+ {
+ net_socket = ipx_socket;
+ }
+ else if (to.type == NA_BROADCAST_IPX)
+ {
+ net_socket = ipx_socket;
+ }
+ else {
+ Com_Error (ERR_FATAL, "NET_SendPacket: bad address type");
+ return;
+ }
+
+ if (!net_socket)
+ return;
+
+ NetadrToSockadr (&to, &addr);
+
+ ret = sendto (net_socket, data, length, 0, (struct sockaddr *)&addr, sizeof(addr) );
+ if (ret == -1)
+ {
+ Com_Printf ("NET_SendPacket ERROR: %s to %s\n", NET_ErrorString(),
+ NET_AdrToString (to));
+ }
+}
+
+
+//=============================================================================
+
+/*
+==================
+Sys_IsLANAddress
+
+LAN clients will have their rate var ignored
+==================
+*/
+qboolean Sys_IsLANAddress (netadr_t adr) {
+ int i;
+
+ if( adr.type == NA_LOOPBACK ) {
+ return qtrue;
+ }
+
+ if( adr.type == NA_IPX ) {
+ return qtrue;
+ }
+
+ if( adr.type != NA_IP ) {
+ return qfalse;
+ }
+
+ // choose which comparison to use based on the class of the address being tested
+ // any local adresses of a different class than the address being tested will fail based on the first byte
+
+ // Class A
+ if( (adr.ip[0] & 0x80) == 0x00 ) {
+ for ( i = 0 ; i < numIP ; i++ ) {
+ if( adr.ip[0] == localIP[i][0] ) {
+ return qtrue;
+ }
+ }
+ // the RFC1918 class a block will pass the above test
+ return qfalse;
+ }
+
+ // Class B
+ if( (adr.ip[0] & 0xc0) == 0x80 ) {
+ for ( i = 0 ; i < numIP ; i++ ) {
+ if( adr.ip[0] == localIP[i][0] && adr.ip[1] == localIP[i][1] ) {
+ return qtrue;
+ }
+ // also check against the RFC1918 class b blocks
+ if( adr.ip[0] == 172 && localIP[i][0] == 172 && (adr.ip[1] & 0xf0) == 16 && (localIP[i][1] & 0xf0) == 16 ) {
+ return qtrue;
+ }
+ }
+ return qfalse;
+ }
+
+ // Class C
+ for ( i = 0 ; i < numIP ; i++ ) {
+ if( adr.ip[0] == localIP[i][0] && adr.ip[1] == localIP[i][1] && adr.ip[2] == localIP[i][2] ) {
+ return qtrue;
+ }
+ // also check against the RFC1918 class c blocks
+ if( adr.ip[0] == 192 && localIP[i][0] == 192 && adr.ip[1] == 168 && localIP[i][1] == 168 ) {
+ return qtrue;
+ }
+ }
+ return qfalse;
+}
+
+/*
+==================
+Sys_ShowIP
+==================
+*/
+void Sys_ShowIP(void) {
+ int i;
+
+ for (i = 0; i < numIP; i++) {
+ Com_Printf( "IP: %i.%i.%i.%i\n", localIP[i][0], localIP[i][1], localIP[i][2], localIP[i][3] );
+ }
+}
+
+/*
+=====================
+NET_GetLocalAddress
+=====================
+*/
+#ifdef MACOS_X
+// Don't do a forward mapping from the hostname of the machine to the IP. The reason is that we might have obtained an IP address from DHCP and there might not be any name registered for the machine. On Mac OS X, the machine name defaults to 'localhost' and NetInfo has 127.0.0.1 listed for this name. Instead, we want to get a list of all the IP network interfaces on the machine.
+// This code adapted from OmniNetworking.
+
+#define IFR_NEXT(ifr) \
+ ((struct ifreq *) ((char *) (ifr) + sizeof(*(ifr)) + \
+ MAX(0, (int) (ifr)->ifr_addr.sa_len - (int) sizeof((ifr)->ifr_addr))))
+
+void NET_GetLocalAddress( void ) {
+ struct ifreq requestBuffer[MAX_IPS], *linkInterface, *inetInterface;
+ struct ifconf ifc;
+ struct ifreq ifr;
+ struct sockaddr_dl *sdl;
+ int interfaceSocket;
+ int family;
+
+ //Com_Printf("NET_GetLocalAddress: Querying for network interfaces\n");
+
+ // Set this early so we can just return if there is an error
+ numIP = 0;
+
+ ifc.ifc_len = sizeof(requestBuffer);
+ ifc.ifc_buf = (caddr_t)requestBuffer;
+
+ // Since we get at this info via an ioctl, we need a temporary little socket. This will only get AF_INET interfaces, but we probably don't care about anything else. If we do end up caring later, we should add a ONAddressFamily and at a -interfaces method to it.
+ family = AF_INET;
+ if ((interfaceSocket = socket(family, SOCK_DGRAM, 0)) < 0) {
+ Com_Printf("NET_GetLocalAddress: Unable to create temporary socket, errno = %d\n", errno);
+ return;
+ }
+
+ if (ioctl(interfaceSocket, SIOCGIFCONF, &ifc) != 0) {
+ Com_Printf("NET_GetLocalAddress: Unable to get list of network interfaces, errno = %d\n", errno);
+ return;
+ }
+
+
+ linkInterface = (struct ifreq *) ifc.ifc_buf;
+ while ((char *) linkInterface < &ifc.ifc_buf[ifc.ifc_len]) {
+ unsigned int nameLength;
+
+ // The ioctl returns both the entries having the address (AF_INET) and the link layer entries (AF_LINK). The AF_LINK entry has the link layer address which contains the interface type. This is the only way I can see to get this information. We cannot assume that we will get bot an AF_LINK and AF_INET entry since the interface may not be configured. For example, if you have a 10Mb port on the motherboard and a 100Mb card, you may not configure the motherboard port.
+
+ // For each AF_LINK entry...
+ if (linkInterface->ifr_addr.sa_family == AF_LINK) {
+ // if there is a matching AF_INET entry
+ inetInterface = (struct ifreq *) ifc.ifc_buf;
+ while ((char *) inetInterface < &ifc.ifc_buf[ifc.ifc_len]) {
+ if (inetInterface->ifr_addr.sa_family == AF_INET &&
+ !strncmp(inetInterface->ifr_name, linkInterface->ifr_name, sizeof(linkInterface->ifr_name))) {
+
+ for (nameLength = 0; nameLength < IFNAMSIZ; nameLength++)
+ if (!linkInterface->ifr_name[nameLength])
+ break;
+
+ sdl = (struct sockaddr_dl *)&linkInterface->ifr_addr;
+ // Skip loopback interfaces
+ if (sdl->sdl_type != IFT_LOOP) {
+ // Get the local interface address
+ strncpy(ifr.ifr_name, inetInterface->ifr_name, sizeof(ifr.ifr_name));
+ if (ioctl(interfaceSocket, OSIOCGIFADDR, (caddr_t)&ifr) < 0) {
+ Com_Printf("NET_GetLocalAddress: Unable to get local address for interface '%s', errno = %d\n", inetInterface->ifr_name, errno);
+ } else {
+ struct sockaddr_in *sin;
+ int ip;
+
+ sin = (struct sockaddr_in *)&ifr.ifr_addr;
+
+ ip = ntohl(sin->sin_addr.s_addr);
+ localIP[ numIP ][0] = (ip >> 24) & 0xff;
+ localIP[ numIP ][1] = (ip >> 16) & 0xff;
+ localIP[ numIP ][2] = (ip >> 8) & 0xff;
+ localIP[ numIP ][3] = (ip >> 0) & 0xff;
+ Com_Printf( "IP: %i.%i.%i.%i (%s)\n", localIP[ numIP ][0], localIP[ numIP ][1], localIP[ numIP ][2], localIP[ numIP ][3], inetInterface->ifr_name);
+ numIP++;
+ }
+ }
+
+ // We will assume that there is only one AF_INET entry per AF_LINK entry.
+ // What happens when we have an interface that has multiple IP addresses, or
+ // can that even happen?
+ // break;
+ }
+ inetInterface = IFR_NEXT(inetInterface);
+ }
+ }
+ linkInterface = IFR_NEXT(linkInterface);
+ }
+
+ close(interfaceSocket);
+}
+
+#else
+void NET_GetLocalAddress( void ) {
+ char hostname[256];
+ struct hostent *hostInfo;
+ // int error; // bk001204 - unused
+ char *p;
+ int ip;
+ int n;
+
+ if ( gethostname( hostname, 256 ) == -1 ) {
+ return;
+ }
+
+ hostInfo = gethostbyname( hostname );
+ if ( !hostInfo ) {
+ return;
+ }
+
+ Com_Printf( "Hostname: %s\n", hostInfo->h_name );
+ n = 0;
+ while( ( p = hostInfo->h_aliases[n++] ) != NULL ) {
+ Com_Printf( "Alias: %s\n", p );
+ }
+
+ if ( hostInfo->h_addrtype != AF_INET ) {
+ return;
+ }
+
+ numIP = 0;
+ while( ( p = hostInfo->h_addr_list[numIP++] ) != NULL && numIP < MAX_IPS ) {
+ ip = ntohl( *(int *)p );
+ localIP[ numIP ][0] = p[0];
+ localIP[ numIP ][1] = p[1];
+ localIP[ numIP ][2] = p[2];
+ localIP[ numIP ][3] = p[3];
+ Com_Printf( "IP: %i.%i.%i.%i\n", ( ip >> 24 ) & 0xff, ( ip >> 16 ) & 0xff, ( ip >> 8 ) & 0xff, ip & 0xff );
+ }
+}
+#endif
+
+/*
+====================
+NET_OpenIP
+====================
+*/
+// bk001204 - prototype needed
+int NET_IPSocket (char *net_interface, int port);
+void NET_OpenIP (void)
+{
+ cvar_t *ip;
+ int port;
+ int i;
+
+ ip = Cvar_Get ("net_ip", "localhost", 0);
+
+ port = Cvar_Get("net_port", va("%i", PORT_SERVER), 0)->value;
+
+ for ( i = 0 ; i < 10 ; i++ ) {
+ ip_socket = NET_IPSocket (ip->string, port + i);
+ if ( ip_socket ) {
+ Cvar_SetValue( "net_port", port + i );
+ NET_GetLocalAddress();
+ return;
+ }
+ }
+ Com_Error (ERR_FATAL, "Couldn't allocate IP port");
+}
+
+
+/*
+====================
+NET_Init
+====================
+*/
+void NET_Init (void)
+{
+ noudp = Cvar_Get ("net_noudp", "0", 0);
+ // open sockets
+ if (! noudp->value) {
+ NET_OpenIP ();
+ }
+}
+
+
+/*
+====================
+NET_IPSocket
+====================
+*/
+int NET_IPSocket (char *net_interface, int port)
+{
+ int newsocket;
+ struct sockaddr_in address;
+ qboolean _qtrue = qtrue;
+ int i = 1;
+
+ if ( net_interface ) {
+ Com_Printf("Opening IP socket: %s:%i\n", net_interface, port );
+ } else {
+ Com_Printf("Opening IP socket: localhost:%i\n", port );
+ }
+
+ if ((newsocket = socket (PF_INET, SOCK_DGRAM, IPPROTO_UDP)) == -1)
+ {
+ Com_Printf ("ERROR: UDP_OpenSocket: socket: %s", NET_ErrorString());
+ return 0;
+ }
+
+ // make it non-blocking
+ if (ioctl (newsocket, FIONBIO, &_qtrue) == -1)
+ {
+ Com_Printf ("ERROR: UDP_OpenSocket: ioctl FIONBIO:%s\n", NET_ErrorString());
+ return 0;
+ }
+
+ // make it broadcast capable
+ if (setsockopt(newsocket, SOL_SOCKET, SO_BROADCAST, (char *)&i, sizeof(i)) == -1)
+ {
+ Com_Printf ("ERROR: UDP_OpenSocket: setsockopt SO_BROADCAST:%s\n", NET_ErrorString());
+ return 0;
+ }
+
+ if (!net_interface || !net_interface[0] || !Q_stricmp(net_interface, "localhost"))
+ address.sin_addr.s_addr = INADDR_ANY;
+ else
+ Sys_StringToSockaddr (net_interface, (struct sockaddr *)&address);
+
+ if (port == PORT_ANY)
+ address.sin_port = 0;
+ else
+ address.sin_port = htons((short)port);
+
+ address.sin_family = AF_INET;
+
+ if( bind (newsocket, (void *)&address, sizeof(address)) == -1)
+ {
+ Com_Printf ("ERROR: UDP_OpenSocket: bind: %s\n", NET_ErrorString());
+ close (newsocket);
+ return 0;
+ }
+
+ return newsocket;
+}
+
+/*
+====================
+NET_Shutdown
+====================
+*/
+void NET_Shutdown (void)
+{
+ if (ip_socket) {
+ close(ip_socket);
+ ip_socket = 0;
+ }
+}
+
+
+/*
+====================
+NET_ErrorString
+====================
+*/
+char *NET_ErrorString (void)
+{
+ int code;
+
+ code = errno;
+ return strerror (code);
+}
+
+// sleeps msec or until net socket is ready
+void NET_Sleep(int msec)
+{
+ struct timeval timeout;
+ fd_set fdset;
+ extern qboolean stdin_active;
+
+ if (!ip_socket || !com_dedicated->integer)
+ return; // we're not a server, just run full speed
+
+ FD_ZERO(&fdset);
+ if (stdin_active)
+ FD_SET(0, &fdset); // stdin is processed too
+ FD_SET(ip_socket, &fdset); // network socket
+ timeout.tv_sec = msec/1000;
+ timeout.tv_usec = (msec%1000)*1000;
+ select(ip_socket+1, &fdset, NULL, NULL, &timeout);
+}
+
diff --git a/code/unix/unix_shared.c b/code/unix/unix_shared.c
new file mode 100755
index 0000000..af67760
--- /dev/null
+++ b/code/unix/unix_shared.c
@@ -0,0 +1,435 @@
+/*
+===========================================================================
+Copyright (C) 1999-2005 Id Software, Inc.
+
+This file is part of Quake III Arena source code.
+
+Quake III Arena source code is free software; you can redistribute it
+and/or modify it under the terms of the GNU General Public License as
+published by the Free Software Foundation; either version 2 of the License,
+or (at your option) any later version.
+
+Quake III Arena source code is distributed in the hope that it will be
+useful, but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with Foobar; if not, write to the Free Software
+Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+===========================================================================
+*/
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <errno.h>
+#include <stdio.h>
+#include <dirent.h>
+#include <unistd.h>
+#include <sys/mman.h>
+#include <sys/time.h>
+#include <pwd.h>
+
+#include "../game/q_shared.h"
+#include "../qcommon/qcommon.h"
+
+//=============================================================================
+
+// Used to determine CD Path
+static char cdPath[MAX_OSPATH];
+
+// Used to determine local installation path
+static char installPath[MAX_OSPATH];
+
+// Used to determine where to store user-specific files
+static char homePath[MAX_OSPATH];
+
+/*
+================
+Sys_Milliseconds
+================
+*/
+/* base time in seconds, that's our origin
+ timeval:tv_sec is an int:
+ assuming this wraps every 0x7fffffff - ~68 years since the Epoch (1970) - we're safe till 2038
+ using unsigned long data type to work right with Sys_XTimeToSysTime */
+unsigned long sys_timeBase = 0;
+/* current time in ms, using sys_timeBase as origin
+ NOTE: sys_timeBase*1000 + curtime -> ms since the Epoch
+ 0x7fffffff ms - ~24 days
+ although timeval:tv_usec is an int, I'm not sure wether it is actually used as an unsigned int
+ (which would affect the wrap period) */
+int curtime;
+int Sys_Milliseconds (void)
+{
+ struct timeval tp;
+
+ gettimeofday(&tp, NULL);
+
+ if (!sys_timeBase)
+ {
+ sys_timeBase = tp.tv_sec;
+ return tp.tv_usec/1000;
+ }
+
+ curtime = (tp.tv_sec - sys_timeBase)*1000 + tp.tv_usec/1000;
+
+ return curtime;
+}
+
+#if defined(__linux__) && !defined(DEDICATED)
+/*
+================
+Sys_XTimeToSysTime
+sub-frame timing of events returned by X
+X uses the Time typedef - unsigned long
+disable with in_subframe 0
+
+ sys_timeBase*1000 is the number of ms since the Epoch of our origin
+ xtime is in ms and uses the Epoch as origin
+ Time data type is an unsigned long: 0xffffffff ms - ~49 days period
+ I didn't find much info in the XWindow documentation about the wrapping
+ we clamp sys_timeBase*1000 to unsigned long, that gives us the current origin for xtime
+ the computation will still work if xtime wraps (at ~49 days period since the Epoch) after we set sys_timeBase
+
+================
+*/
+extern cvar_t *in_subframe;
+int Sys_XTimeToSysTime (unsigned long xtime)
+{
+ int ret, time, test;
+
+ if (!in_subframe->value)
+ {
+ // if you don't want to do any event times corrections
+ return Sys_Milliseconds();
+ }
+
+ // test the wrap issue
+#if 0
+ // reference values for test: sys_timeBase 0x3dc7b5e9 xtime 0x541ea451 (read these from a test run)
+ // xtime will wrap in 0xabe15bae ms >~ 0x2c0056 s (33 days from Nov 5 2002 -> 8 Dec)
+ // NOTE: date -d '1970-01-01 UTC 1039384002 seconds' +%c
+ // use sys_timeBase 0x3dc7b5e9+0x2c0056 = 0x3df3b63f
+ // after around 5s, xtime would have wrapped around
+ // we get 7132, the formula handles the wrap safely
+ unsigned long xtime_aux,base_aux;
+ int test;
+// Com_Printf("sys_timeBase: %p\n", sys_timeBase);
+// Com_Printf("xtime: %p\n", xtime);
+ xtime_aux = 500; // 500 ms after wrap
+ base_aux = 0x3df3b63f; // the base a few seconds before wrap
+ test = xtime_aux - (unsigned long)(base_aux*1000);
+ Com_Printf("xtime wrap test: %d\n", test);
+#endif
+
+ // some X servers (like suse 8.1's) report weird event times
+ // if the game is loading, resolving DNS, etc. we are also getting old events
+ // so we only deal with subframe corrections that look 'normal'
+ ret = xtime - (unsigned long)(sys_timeBase*1000);
+ time = Sys_Milliseconds();
+ test = time - ret;
+ //printf("delta: %d\n", test);
+ if (test < 0 || test > 30) // in normal conditions I've never seen this go above
+ {
+ return time;
+ }
+
+ return ret;
+}
+#endif
+
+//#if 0 // bk001215 - see snapvector.nasm for replacement
+#if (defined __APPLE__) // rcg010206 - using this for PPC builds...
+long fastftol( float f ) { // bk001213 - from win32/win_shared.c
+ //static int tmp;
+ // __asm fld f
+ //__asm fistp tmp
+ //__asm mov eax, tmp
+ return (long)f;
+}
+
+void Sys_SnapVector( float *v ) { // bk001213 - see win32/win_shared.c
+ // bk001213 - old linux
+ v[0] = rint(v[0]);
+ v[1] = rint(v[1]);
+ v[2] = rint(v[2]);
+}
+#endif
+
+
+void Sys_Mkdir( const char *path )
+{
+ mkdir (path, 0777);
+}
+
+char *strlwr (char *s) {
+ if ( s==NULL ) { // bk001204 - paranoia
+ assert(0);
+ return s;
+ }
+ while (*s) {
+ *s = tolower(*s);
+ s++;
+ }
+ return s; // bk001204 - duh
+}
+
+//============================================
+
+#define MAX_FOUND_FILES 0x1000
+
+// bk001129 - new in 1.26
+void Sys_ListFilteredFiles( const char *basedir, char *subdirs, char *filter, char **list, int *numfiles ) {
+ char search[MAX_OSPATH], newsubdirs[MAX_OSPATH];
+ char filename[MAX_OSPATH];
+ DIR *fdir;
+ struct dirent *d;
+ struct stat st;
+
+ if ( *numfiles >= MAX_FOUND_FILES - 1 ) {
+ return;
+ }
+
+ if (strlen(subdirs)) {
+ Com_sprintf( search, sizeof(search), "%s/%s", basedir, subdirs );
+ }
+ else {
+ Com_sprintf( search, sizeof(search), "%s", basedir );
+ }
+
+ if ((fdir = opendir(search)) == NULL) {
+ return;
+ }
+
+ while ((d = readdir(fdir)) != NULL) {
+ Com_sprintf(filename, sizeof(filename), "%s/%s", search, d->d_name);
+ if (stat(filename, &st) == -1)
+ continue;
+
+ if (st.st_mode & S_IFDIR) {
+ if (Q_stricmp(d->d_name, ".") && Q_stricmp(d->d_name, "..")) {
+ if (strlen(subdirs)) {
+ Com_sprintf( newsubdirs, sizeof(newsubdirs), "%s/%s", subdirs, d->d_name);
+ }
+ else {
+ Com_sprintf( newsubdirs, sizeof(newsubdirs), "%s", d->d_name);
+ }
+ Sys_ListFilteredFiles( basedir, newsubdirs, filter, list, numfiles );
+ }
+ }
+ if ( *numfiles >= MAX_FOUND_FILES - 1 ) {
+ break;
+ }
+ Com_sprintf( filename, sizeof(filename), "%s/%s", subdirs, d->d_name );
+ if (!Com_FilterPath( filter, filename, qfalse ))
+ continue;
+ list[ *numfiles ] = CopyString( filename );
+ (*numfiles)++;
+ }
+
+ closedir(fdir);
+}
+
+// bk001129 - in 1.17 this used to be
+// char **Sys_ListFiles( const char *directory, const char *extension, int *numfiles, qboolean wantsubs )
+char **Sys_ListFiles( const char *directory, const char *extension, char *filter, int *numfiles, qboolean wantsubs )
+{
+ struct dirent *d;
+ // char *p; // bk001204 - unused
+ DIR *fdir;
+ qboolean dironly = wantsubs;
+ char search[MAX_OSPATH];
+ int nfiles;
+ char **listCopy;
+ char *list[MAX_FOUND_FILES];
+ //int flag; // bk001204 - unused
+ int i;
+ struct stat st;
+
+ int extLen;
+
+ if (filter) {
+
+ nfiles = 0;
+ Sys_ListFilteredFiles( directory, "", filter, list, &nfiles );
+
+ list[ nfiles ] = 0;
+ *numfiles = nfiles;
+
+ if (!nfiles)
+ return NULL;
+
+ listCopy = Z_Malloc( ( nfiles + 1 ) * sizeof( *listCopy ) );
+ for ( i = 0 ; i < nfiles ; i++ ) {
+ listCopy[i] = list[i];
+ }
+ listCopy[i] = NULL;
+
+ return listCopy;
+ }
+
+ if ( !extension)
+ extension = "";
+
+ if ( extension[0] == '/' && extension[1] == 0 ) {
+ extension = "";
+ dironly = qtrue;
+ }
+
+ extLen = strlen( extension );
+
+ // search
+ nfiles = 0;
+
+ if ((fdir = opendir(directory)) == NULL) {
+ *numfiles = 0;
+ return NULL;
+ }
+
+ while ((d = readdir(fdir)) != NULL) {
+ Com_sprintf(search, sizeof(search), "%s/%s", directory, d->d_name);
+ if (stat(search, &st) == -1)
+ continue;
+ if ((dironly && !(st.st_mode & S_IFDIR)) ||
+ (!dironly && (st.st_mode & S_IFDIR)))
+ continue;
+
+ if (*extension) {
+ if ( strlen( d->d_name ) < strlen( extension ) ||
+ Q_stricmp(
+ d->d_name + strlen( d->d_name ) - strlen( extension ),
+ extension ) ) {
+ continue; // didn't match
+ }
+ }
+
+ if ( nfiles == MAX_FOUND_FILES - 1 )
+ break;
+ list[ nfiles ] = CopyString( d->d_name );
+ nfiles++;
+ }
+
+ list[ nfiles ] = 0;
+
+ closedir(fdir);
+
+ // return a copy of the list
+ *numfiles = nfiles;
+
+ if ( !nfiles ) {
+ return NULL;
+ }
+
+ listCopy = Z_Malloc( ( nfiles + 1 ) * sizeof( *listCopy ) );
+ for ( i = 0 ; i < nfiles ; i++ ) {
+ listCopy[i] = list[i];
+ }
+ listCopy[i] = NULL;
+
+ return listCopy;
+}
+
+void Sys_FreeFileList( char **list ) {
+ int i;
+
+ if ( !list ) {
+ return;
+ }
+
+ for ( i = 0 ; list[i] ; i++ ) {
+ Z_Free( list[i] );
+ }
+
+ Z_Free( list );
+}
+
+char *Sys_Cwd( void )
+{
+ static char cwd[MAX_OSPATH];
+
+ getcwd( cwd, sizeof( cwd ) - 1 );
+ cwd[MAX_OSPATH-1] = 0;
+
+ return cwd;
+}
+
+void Sys_SetDefaultCDPath(const char *path)
+{
+ Q_strncpyz(cdPath, path, sizeof(cdPath));
+}
+
+char *Sys_DefaultCDPath(void)
+{
+ return cdPath;
+}
+
+void Sys_SetDefaultInstallPath(const char *path)
+{
+ Q_strncpyz(installPath, path, sizeof(installPath));
+}
+
+char *Sys_DefaultInstallPath(void)
+{
+ if (*installPath)
+ return installPath;
+ else
+ return Sys_Cwd();
+}
+
+void Sys_SetDefaultHomePath(const char *path)
+{
+ Q_strncpyz(homePath, path, sizeof(homePath));
+}
+
+char *Sys_DefaultHomePath(void)
+{
+ char *p;
+
+ if (*homePath)
+ return homePath;
+
+ if ((p = getenv("HOME")) != NULL) {
+ Q_strncpyz(homePath, p, sizeof(homePath));
+#ifdef MACOS_X
+ Q_strcat(homePath, sizeof(homePath), "/Library/Application Support/Quake3");
+#else
+ Q_strcat(homePath, sizeof(homePath), "/.q3a");
+#endif
+ if (mkdir(homePath, 0777)) {
+ if (errno != EEXIST)
+ Sys_Error("Unable to create directory \"%s\", error is %s(%d)\n", homePath, strerror(errno), errno);
+ }
+ return homePath;
+ }
+ return ""; // assume current dir
+}
+
+//============================================
+
+int Sys_GetProcessorId( void )
+{
+ return CPUID_GENERIC;
+}
+
+void Sys_ShowConsole( int visLevel, qboolean quitOnClose )
+{
+}
+
+char *Sys_GetCurrentUser( void )
+{
+ struct passwd *p;
+
+ if ( (p = getpwuid( getuid() )) == NULL ) {
+ return "player";
+ }
+ return p->pw_name;
+}
+
+#if defined(__linux__)
+// TTimo
+// sysconf() in libc, POSIX.1 compliant
+unsigned int Sys_ProcessorCount()
+{
+ return sysconf(_SC_NPROCESSORS_ONLN);
+}
+#endif
diff --git a/code/unix/vm_x86.c b/code/unix/vm_x86.c
new file mode 100755
index 0000000..de79d36
--- /dev/null
+++ b/code/unix/vm_x86.c
@@ -0,0 +1,29 @@
+/*
+===========================================================================
+Copyright (C) 1999-2005 Id Software, Inc.
+
+This file is part of Quake III Arena source code.
+
+Quake III Arena source code is free software; you can redistribute it
+and/or modify it under the terms of the GNU General Public License as
+published by the Free Software Foundation; either version 2 of the License,
+or (at your option) any later version.
+
+Quake III Arena source code is distributed in the hope that it will be
+useful, but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with Foobar; if not, write to the Free Software
+Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+===========================================================================
+*/
+
+#include "../qcommon/vm_local.h"
+
+void VM_Compile( vm_t *vm, vmHeader_t *header ) {}
+int VM_CallCompiled( vm_t *vm, int *args ) {}
+
+
+
diff --git a/code/unix/vm_x86a.s b/code/unix/vm_x86a.s
new file mode 100755
index 0000000..a85e452
--- /dev/null
+++ b/code/unix/vm_x86a.s
@@ -0,0 +1,462 @@
+/*
+===========================================================================
+Copyright (C) 1999-2005 Id Software, Inc.
+
+This file is part of Quake III Arena source code.
+
+Quake III Arena source code is free software; you can redistribute it
+and/or modify it under the terms of the GNU General Public License as
+published by the Free Software Foundation; either version 2 of the License,
+or (at your option) any later version.
+
+Quake III Arena source code is distributed in the hope that it will be
+useful, but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with Foobar; if not, write to the Free Software
+Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+===========================================================================
+*/
+//
+// vm_x86a.s
+// linux x86 vm support
+
+#include "qasm.h"
+
+.extern C(instructionPointers)
+
+ .data
+
+ .align 4
+
+programStack .long 0
+opStack .long 0
+syscallNum .long 0
+
+ .text
+
+.globl C(AsmCAll)
+C(AsmCall):
+ movl (%edi),%eax
+ subl 4,%edi
+ orl %eax,%eax
+ jl systemCall
+ // calling another vm function
+ shll 2,%eax
+ addl C(instructionPointers),%eax
+ call (%eax)
+ ret
+systemCall:
+
+ // convert negative num to system call number
+ // and store right before the first arg
+ negl %eax
+ decl %eax
+
+ movl %eax,syscallNum
+///---- UNFINISHED FROM HERE
+ mov dword ptr syscallNum, eax // so C code can get at it
+ mov dword ptr programStack, esi // so C code can get at it
+ mov dword ptr opStack, edi
+
+ push ecx
+ push esi // we may call recursively, so the
+ push edi // statics aren't guaranteed to be around
+}
+ // save the stack to allow recursive VM entry
+ currentVM->programStack = programStack - 4;
+ *(int *)((byte *)currentVM->dataBase + programStack + 4) = syscallNum;
+//VM_LogSyscalls( (int *)((byte *)currentVM->dataBase + programStack + 4) );
+ *(opStack+1) = currentVM->systemCall( (int *)((byte *)currentVM->dataBase + programStack + 4) );
+
+_asm {
+ pop edi
+ pop esi
+ pop ecx
+ add edi, 4 // we added the return value
+
+ ret
+}
+
+}
+
+//--------------------------------------------
+ movl val(%esp),%ecx
+ movl $0x100,%edx // 0x10000000000 as dividend
+ cmpl %edx,%ecx
+ jle LOutOfRange
+
+ subl %eax,%eax
+ divl %ecx
+
+ ret
+
+LOutOfRange:
+ movl $0xFFFFFFFF,%eax
+ ret
+
+#if 0
+
+#define in 4
+#define out 8
+
+ .align 2
+.globl C(TransformVector)
+C(TransformVector):
+ movl in(%esp),%eax
+ movl out(%esp),%edx
+
+ flds (%eax) // in[0]
+ fmuls C(vright) // in[0]*vright[0]
+ flds (%eax) // in[0] | in[0]*vright[0]
+ fmuls C(vup) // in[0]*vup[0] | in[0]*vright[0]
+ flds (%eax) // in[0] | in[0]*vup[0] | in[0]*vright[0]
+ fmuls C(vpn) // in[0]*vpn[0] | in[0]*vup[0] | in[0]*vright[0]
+
+ flds 4(%eax) // in[1] | ...
+ fmuls C(vright)+4 // in[1]*vright[1] | ...
+ flds 4(%eax) // in[1] | in[1]*vright[1] | ...
+ fmuls C(vup)+4 // in[1]*vup[1] | in[1]*vright[1] | ...
+ flds 4(%eax) // in[1] | in[1]*vup[1] | in[1]*vright[1] | ...
+ fmuls C(vpn)+4 // in[1]*vpn[1] | in[1]*vup[1] | in[1]*vright[1] | ...
+ fxch %st(2) // in[1]*vright[1] | in[1]*vup[1] | in[1]*vpn[1] | ...
+
+ faddp %st(0),%st(5) // in[1]*vup[1] | in[1]*vpn[1] | ...
+ faddp %st(0),%st(3) // in[1]*vpn[1] | ...
+ faddp %st(0),%st(1) // vpn_accum | vup_accum | vright_accum
+
+ flds 8(%eax) // in[2] | ...
+ fmuls C(vright)+8 // in[2]*vright[2] | ...
+ flds 8(%eax) // in[2] | in[2]*vright[2] | ...
+ fmuls C(vup)+8 // in[2]*vup[2] | in[2]*vright[2] | ...
+ flds 8(%eax) // in[2] | in[2]*vup[2] | in[2]*vright[2] | ...
+ fmuls C(vpn)+8 // in[2]*vpn[2] | in[2]*vup[2] | in[2]*vright[2] | ...
+ fxch %st(2) // in[2]*vright[2] | in[2]*vup[2] | in[2]*vpn[2] | ...
+
+ faddp %st(0),%st(5) // in[2]*vup[2] | in[2]*vpn[2] | ...
+ faddp %st(0),%st(3) // in[2]*vpn[2] | ...
+ faddp %st(0),%st(1) // vpn_accum | vup_accum | vright_accum
+
+ fstps 8(%edx) // out[2]
+ fstps 4(%edx) // out[1]
+ fstps (%edx) // out[0]
+
+ ret
+
+#endif
+
+#define EMINS 4+4
+#define EMAXS 4+8
+#define P 4+12
+
+ .align 2
+.globl C(BoxOnPlaneSide)
+C(BoxOnPlaneSide):
+ pushl %ebx
+
+ movl P(%esp),%edx
+ movl EMINS(%esp),%ecx
+ xorl %eax,%eax
+ movl EMAXS(%esp),%ebx
+ movb pl_signbits(%edx),%al
+ cmpb $8,%al
+ jge Lerror
+ flds pl_normal(%edx) // p->normal[0]
+ fld %st(0) // p->normal[0] | p->normal[0]
+ jmp Ljmptab(,%eax,4)
+
+
+//dist1= p->normal[0]*emaxs[0] + p->normal[1]*emaxs[1] + p->normal[2]*emaxs[2];
+//dist2= p->normal[0]*emins[0] + p->normal[1]*emins[1] + p->normal[2]*emins[2];
+Lcase0:
+ fmuls (%ebx) // p->normal[0]*emaxs[0] | p->normal[0]
+ flds pl_normal+4(%edx) // p->normal[1] | p->normal[0]*emaxs[0] |
+ // p->normal[0]
+ fxch %st(2) // p->normal[0] | p->normal[0]*emaxs[0] |
+ // p->normal[1]
+ fmuls (%ecx) // p->normal[0]*emins[0] |
+ // p->normal[0]*emaxs[0] | p->normal[1]
+ fxch %st(2) // p->normal[1] | p->normal[0]*emaxs[0] |
+ // p->normal[0]*emins[0]
+ fld %st(0) // p->normal[1] | p->normal[1] |
+ // p->normal[0]*emaxs[0] |
+ // p->normal[0]*emins[0]
+ fmuls 4(%ebx) // p->normal[1]*emaxs[1] | p->normal[1] |
+ // p->normal[0]*emaxs[0] |
+ // p->normal[0]*emins[0]
+ flds pl_normal+8(%edx) // p->normal[2] | p->normal[1]*emaxs[1] |
+ // p->normal[1] | p->normal[0]*emaxs[0] |
+ // p->normal[0]*emins[0]
+ fxch %st(2) // p->normal[1] | p->normal[1]*emaxs[1] |
+ // p->normal[2] | p->normal[0]*emaxs[0] |
+ // p->normal[0]*emins[0]
+ fmuls 4(%ecx) // p->normal[1]*emins[1] |
+ // p->normal[1]*emaxs[1] |
+ // p->normal[2] | p->normal[0]*emaxs[0] |
+ // p->normal[0]*emins[0]
+ fxch %st(2) // p->normal[2] | p->normal[1]*emaxs[1] |
+ // p->normal[1]*emins[1] |
+ // p->normal[0]*emaxs[0] |
+ // p->normal[0]*emins[0]
+ fld %st(0) // p->normal[2] | p->normal[2] |
+ // p->normal[1]*emaxs[1] |
+ // p->normal[1]*emins[1] |
+ // p->normal[0]*emaxs[0] |
+ // p->normal[0]*emins[0]
+ fmuls 8(%ebx) // p->normal[2]*emaxs[2] |
+ // p->normal[2] |
+ // p->normal[1]*emaxs[1] |
+ // p->normal[1]*emins[1] |
+ // p->normal[0]*emaxs[0] |
+ // p->normal[0]*emins[0]
+ fxch %st(5) // p->normal[0]*emins[0] |
+ // p->normal[2] |
+ // p->normal[1]*emaxs[1] |
+ // p->normal[1]*emins[1] |
+ // p->normal[0]*emaxs[0] |
+ // p->normal[2]*emaxs[2]
+ faddp %st(0),%st(3) //p->normal[2] |
+ // p->normal[1]*emaxs[1] |
+ // p->normal[1]*emins[1]+p->normal[0]*emins[0]|
+ // p->normal[0]*emaxs[0] |
+ // p->normal[2]*emaxs[2]
+ fmuls 8(%ecx) //p->normal[2]*emins[2] |
+ // p->normal[1]*emaxs[1] |
+ // p->normal[1]*emins[1]+p->normal[0]*emins[0]|
+ // p->normal[0]*emaxs[0] |
+ // p->normal[2]*emaxs[2]
+ fxch %st(1) //p->normal[1]*emaxs[1] |
+ // p->normal[2]*emins[2] |
+ // p->normal[1]*emins[1]+p->normal[0]*emins[0]|
+ // p->normal[0]*emaxs[0] |
+ // p->normal[2]*emaxs[2]
+ faddp %st(0),%st(3) //p->normal[2]*emins[2] |
+ // p->normal[1]*emins[1]+p->normal[0]*emins[0]|
+ // p->normal[0]*emaxs[0]+p->normal[1]*emaxs[1]|
+ // p->normal[2]*emaxs[2]
+ fxch %st(3) //p->normal[2]*emaxs[2] +
+ // p->normal[1]*emins[1]+p->normal[0]*emins[0]|
+ // p->normal[0]*emaxs[0]+p->normal[1]*emaxs[1]|
+ // p->normal[2]*emins[2]
+ faddp %st(0),%st(2) //p->normal[1]*emins[1]+p->normal[0]*emins[0]|
+ // dist1 | p->normal[2]*emins[2]
+
+ jmp LSetSides
+
+//dist1= p->normal[0]*emins[0] + p->normal[1]*emaxs[1] + p->normal[2]*emaxs[2];
+//dist2= p->normal[0]*emaxs[0] + p->normal[1]*emins[1] + p->normal[2]*emins[2];
+Lcase1:
+ fmuls (%ecx) // emins[0]
+ flds pl_normal+4(%edx)
+ fxch %st(2)
+ fmuls (%ebx) // emaxs[0]
+ fxch %st(2)
+ fld %st(0)
+ fmuls 4(%ebx) // emaxs[1]
+ flds pl_normal+8(%edx)
+ fxch %st(2)
+ fmuls 4(%ecx) // emins[1]
+ fxch %st(2)
+ fld %st(0)
+ fmuls 8(%ebx) // emaxs[2]
+ fxch %st(5)
+ faddp %st(0),%st(3)
+ fmuls 8(%ecx) // emins[2]
+ fxch %st(1)
+ faddp %st(0),%st(3)
+ fxch %st(3)
+ faddp %st(0),%st(2)
+
+ jmp LSetSides
+
+//dist1= p->normal[0]*emaxs[0] + p->normal[1]*emins[1] + p->normal[2]*emaxs[2];
+//dist2= p->normal[0]*emins[0] + p->normal[1]*emaxs[1] + p->normal[2]*emins[2];
+Lcase2:
+ fmuls (%ebx) // emaxs[0]
+ flds pl_normal+4(%edx)
+ fxch %st(2)
+ fmuls (%ecx) // emins[0]
+ fxch %st(2)
+ fld %st(0)
+ fmuls 4(%ecx) // emins[1]
+ flds pl_normal+8(%edx)
+ fxch %st(2)
+ fmuls 4(%ebx) // emaxs[1]
+ fxch %st(2)
+ fld %st(0)
+ fmuls 8(%ebx) // emaxs[2]
+ fxch %st(5)
+ faddp %st(0),%st(3)
+ fmuls 8(%ecx) // emins[2]
+ fxch %st(1)
+ faddp %st(0),%st(3)
+ fxch %st(3)
+ faddp %st(0),%st(2)
+
+ jmp LSetSides
+
+//dist1= p->normal[0]*emins[0] + p->normal[1]*emins[1] + p->normal[2]*emaxs[2];
+//dist2= p->normal[0]*emaxs[0] + p->normal[1]*emaxs[1] + p->normal[2]*emins[2];
+Lcase3:
+ fmuls (%ecx) // emins[0]
+ flds pl_normal+4(%edx)
+ fxch %st(2)
+ fmuls (%ebx) // emaxs[0]
+ fxch %st(2)
+ fld %st(0)
+ fmuls 4(%ecx) // emins[1]
+ flds pl_normal+8(%edx)
+ fxch %st(2)
+ fmuls 4(%ebx) // emaxs[1]
+ fxch %st(2)
+ fld %st(0)
+ fmuls 8(%ebx) // emaxs[2]
+ fxch %st(5)
+ faddp %st(0),%st(3)
+ fmuls 8(%ecx) // emins[2]
+ fxch %st(1)
+ faddp %st(0),%st(3)
+ fxch %st(3)
+ faddp %st(0),%st(2)
+
+ jmp LSetSides
+
+//dist1= p->normal[0]*emaxs[0] + p->normal[1]*emaxs[1] + p->normal[2]*emins[2];
+//dist2= p->normal[0]*emins[0] + p->normal[1]*emins[1] + p->normal[2]*emaxs[2];
+Lcase4:
+ fmuls (%ebx) // emaxs[0]
+ flds pl_normal+4(%edx)
+ fxch %st(2)
+ fmuls (%ecx) // emins[0]
+ fxch %st(2)
+ fld %st(0)
+ fmuls 4(%ebx) // emaxs[1]
+ flds pl_normal+8(%edx)
+ fxch %st(2)
+ fmuls 4(%ecx) // emins[1]
+ fxch %st(2)
+ fld %st(0)
+ fmuls 8(%ecx) // emins[2]
+ fxch %st(5)
+ faddp %st(0),%st(3)
+ fmuls 8(%ebx) // emaxs[2]
+ fxch %st(1)
+ faddp %st(0),%st(3)
+ fxch %st(3)
+ faddp %st(0),%st(2)
+
+ jmp LSetSides
+
+//dist1= p->normal[0]*emins[0] + p->normal[1]*emaxs[1] + p->normal[2]*emins[2];
+//dist2= p->normal[0]*emaxs[0] + p->normal[1]*emins[1] + p->normal[2]*emaxs[2];
+Lcase5:
+ fmuls (%ecx) // emins[0]
+ flds pl_normal+4(%edx)
+ fxch %st(2)
+ fmuls (%ebx) // emaxs[0]
+ fxch %st(2)
+ fld %st(0)
+ fmuls 4(%ebx) // emaxs[1]
+ flds pl_normal+8(%edx)
+ fxch %st(2)
+ fmuls 4(%ecx) // emins[1]
+ fxch %st(2)
+ fld %st(0)
+ fmuls 8(%ecx) // emins[2]
+ fxch %st(5)
+ faddp %st(0),%st(3)
+ fmuls 8(%ebx) // emaxs[2]
+ fxch %st(1)
+ faddp %st(0),%st(3)
+ fxch %st(3)
+ faddp %st(0),%st(2)
+
+ jmp LSetSides
+
+//dist1= p->normal[0]*emaxs[0] + p->normal[1]*emins[1] + p->normal[2]*emins[2];
+//dist2= p->normal[0]*emins[0] + p->normal[1]*emaxs[1] + p->normal[2]*emaxs[2];
+Lcase6:
+ fmuls (%ebx) // emaxs[0]
+ flds pl_normal+4(%edx)
+ fxch %st(2)
+ fmuls (%ecx) // emins[0]
+ fxch %st(2)
+ fld %st(0)
+ fmuls 4(%ecx) // emins[1]
+ flds pl_normal+8(%edx)
+ fxch %st(2)
+ fmuls 4(%ebx) // emaxs[1]
+ fxch %st(2)
+ fld %st(0)
+ fmuls 8(%ecx) // emins[2]
+ fxch %st(5)
+ faddp %st(0),%st(3)
+ fmuls 8(%ebx) // emaxs[2]
+ fxch %st(1)
+ faddp %st(0),%st(3)
+ fxch %st(3)
+ faddp %st(0),%st(2)
+
+ jmp LSetSides
+
+//dist1= p->normal[0]*emins[0] + p->normal[1]*emins[1] + p->normal[2]*emins[2];
+//dist2= p->normal[0]*emaxs[0] + p->normal[1]*emaxs[1] + p->normal[2]*emaxs[2];
+Lcase7:
+ fmuls (%ecx) // emins[0]
+ flds pl_normal+4(%edx)
+ fxch %st(2)
+ fmuls (%ebx) // emaxs[0]
+ fxch %st(2)
+ fld %st(0)
+ fmuls 4(%ecx) // emins[1]
+ flds pl_normal+8(%edx)
+ fxch %st(2)
+ fmuls 4(%ebx) // emaxs[1]
+ fxch %st(2)
+ fld %st(0)
+ fmuls 8(%ecx) // emins[2]
+ fxch %st(5)
+ faddp %st(0),%st(3)
+ fmuls 8(%ebx) // emaxs[2]
+ fxch %st(1)
+ faddp %st(0),%st(3)
+ fxch %st(3)
+ faddp %st(0),%st(2)
+
+LSetSides:
+
+// sides = 0;
+// if (dist1 >= p->dist)
+// sides = 1;
+// if (dist2 < p->dist)
+// sides |= 2;
+
+ faddp %st(0),%st(2) // dist1 | dist2
+ fcomps pl_dist(%edx)
+ xorl %ecx,%ecx
+ fnstsw %ax
+ fcomps pl_dist(%edx)
+ andb $1,%ah
+ xorb $1,%ah
+ addb %ah,%cl
+
+ fnstsw %ax
+ andb $1,%ah
+ addb %ah,%ah
+ addb %ah,%cl
+
+// return sides;
+
+ popl %ebx
+ movl %ecx,%eax // return status
+
+ ret
+
+
+Lerror:
+ movl 1, %eax
+ ret
+
+#endif // id386