% - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
%
% Some basic definitions.
%
% - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -


% some key codes
/keyEsc       0x0000001b def
/keyEnter     0x0000000d def
/keyTab       0x00000009 def
/keyShiftTab  0x0f000000 def
/keyF1        0x3b000000 def
/keyF2        0x3c000000 def
/keyF3        0x3d000000 def
/keyF4        0x3e000000 def
/keyF5        0x3f000000 def
/keyF6        0x40000000 def
/keyF7        0x41000000 def
/keyF8        0x42000000 def
/keyF9        0x43000000 def
/keyF10       0x44000000 def
/keyF11       0x85000000 def
/keyF12       0x86000000 def
/keyHome      0x47000000 def
/keyUp        0x48000000 def
/keyPgUp      0x49000000 def
/keyLeft      0x4b000000 def
/keyRight     0x4d000000 def
/keyEnd       0x4f000000 def
/keyDown      0x50000000 def
/keyPgDown    0x51000000 def
/keyIns       0x52000000 def
/keyDel       0x53000000 def
/keyShiftF1   0x54000000 def
/keyShiftF2   0x55000000 def
/keyShiftF3   0x56000000 def
/keyShiftF4   0x57000000 def
/keyShiftF5   0x58000000 def
/keyShiftF6   0x59000000 def
/keyShiftF7   0x5a000000 def
/keyShiftF8   0x5b000000 def
/keyShiftF9   0x5c000000 def
/keyShiftF10  0x5d000000 def
/keyShiftF11  0x87000000 def
/keyShiftF12  0x88000000 def
/keyCtrlF1    0x5e000000 def
/keyCtrlF2    0x5f000000 def
/keyCtrlF3    0x60000000 def
/keyCtrlF4    0x61000000 def
/keyCtrlF5    0x62000000 def
/keyCtrlF6    0x63000000 def
/keyCtrlF7    0x64000000 def
/keyCtrlF8    0x65000000 def
/keyCtrlF9    0x66000000 def
/keyCtrlF10   0x67000000 def
/keyAltF1     0x68000000 def
/keyAltF2     0x69000000 def
/keyAltF3     0x6a000000 def
/keyAltF4     0x6b000000 def
/keyAltF5     0x6c000000 def
/keyAltF6     0x6d000000 def
/keyAltF7     0x6e000000 def
/keyAltF8     0x6f000000 def
/keyAltF9     0x70000000 def
/keyAltF10    0x71000000 def
/keyCtrlLeft  0x73000000 def
/keyCtrlRight 0x74000000 def
/keyCtrlEnd   0x75000000 def
/keyCtrlDown  0x76000000 def
/keyCtrlHome  0x76000000 def
/keyCtrlUp    0x84000000 def
/keyStatus    0xff000000 def

/statusAlt    0x0208 def
/statusAltL   0x0200 def
/statusAltR   0x0008 def
/statusCtrl   0x0104 def
/statusShift  0x0003 def

/CapsLock { 0x417 cvp getbyte 0x40 and 0 ne } def

/black 0 def
/white 0xffffff def

% input object fields
/.inp_x		 0 def		% x pos
/.inp_y		 1 def		% y pos
/.inp_back	 2 def		% background pixmap
/.inp_buf	 3 def		% input buffer
/.inp_buf_len	 4 def		% input buffer length
/.inp_int	 5 def		% internal state array, see below
% optional fields
/.inp_hidden	 6 def		% hidden buffer
/.inp_label	 7 def		% input field label
/.inp_visible	 8 def		% field is visible
/.inp_show	 9 def		% field should be visible

/.inp_int_cur		0 def	% current edit char offset
/.inp_int_cursor	1 def	% cursor pos (pixel)
/.inp_int_shift		2 def	% input line shifted (pixel)
/.inp_int_flags		3 def	% bit 0: cursor visible
/.inp_int_saved_cursor	4 def	% saved cursor background

% boot loader

% - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
% bootloader - boot loader type
%
% group: system
%
% ( -- int1 )
%
% int1: boot loader type (0: lilo, 1:syslinux/isolinux, 2: grub)
%
/bootloader sysconfig getbyte def

/lilo     bootloader 0 eq def
/syslinux bootloader 1 eq def
/grub     bootloader 2 eq def


% - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
% boot_failsafe - failsafe options the user selected (bitmask)
%
% group: system
%
% ( -- int1 )
%
% int1: option bitmask
%   bit 0: SHIFT pressed
%   bit 1: no graphics
%   bit 2: no monitor detection
%
/boot_failsafe sysconfig 3 add getbyte def


% - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
% bootdrive - drive the BIOS booted from
%
% group: system
%
% ( -- int1 )
%
% int1: BIOS drive id
%
/bootdrive sysconfig 5 add getbyte def


% - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
% mediatype - type of media we booted from
%
% group: system
%
% ( -- int1 )
%
% int1: media type (0 disk, 1 floppy, 2 cdrom)
%
/mediatype sysconfig 2 add getbyte def

/m_disk   0 def
/m_floppy 1 def
/m_cdrom  2 def


% - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
% biosmem - BIOS reported memory size
%
% group: mem
%
% ( -- int1 )
%
% int1: total memory size according to BIOS
%
/biosmem sysconfig 20 add getdword def


% - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
% sectorsize - sector size
%
% group: mem system
%
% ( -- int1 )
%
% int1: sector size in bytes
%
/sectorsize
  1
  sysconfig 1 add getbyte
  20 min	% max. 1 MB
  dup 0 eq { pop 9 } if
  shl
def


% - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
% getinfo - type of info box
%
% group: system
%
% ( int1 -- int2 )
%
% int1: type of info box we have to show
% int2: some data
%
% Note: really weird, should be replaced by something more obvious.
%
/getinfo {
  2 shl
  sysconfig 12 add exch add getdword
} def


% bool values
/true     0 0 eq def
/false    0 0 ne def

% type values
/t_none		 0 def
/t_int		 1 def
/t_unsigned	 2 def
/t_bool		 3 def
/t_string	 4 def
/t_code		 5 def
/t_ret		 6 def
/t_prim		 7 def
/t_sec		 8 def
/t_dict_idx	 9 def
/t_array	10 def
/t_end		11 def
/t_ptr		12 def

/.value { t_int settype } def
/.undef 0 t_none settype def
/.end 0 t_end settype def


% - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
% Print string (for debugging).
%
% ( string ) ==> ( )
%
/string.print {
  dup
  currentpoint currentpoint 5 -1 roll strsize image moveto
  show
} def


% - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
% Print number (for debugging).
%
% ( number ) ==> ( )
%
/number.print {
  32 string
  exch over
  "%08x" exch sprintf
  dup string.print
  free
} def


% - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
% Print obj (for debugging).
%
% ( obj ) ==> ( )
%
/obj.print {
  64 string
  exch dup
  .value exch gettype
  "%x:%08x" 3 index sprintf
  dup string.print
  free
} def


% - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
% Print (for debugging).
%
% ( obj ) ==> ( )
%
/print {
  dup gettype t_int eq { number.print return } if
  dup gettype t_string eq { string.print return } if
  obj.print
} def


% - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
% Convert object to pointer.
%
% ( obj ) ==> ( ptr )
%
/cvp { t_ptr settype } def


% - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
% Convert object to string.
%
% ( obj ) ==> ( string )
%
/cvs { t_string settype } def


% base num char

% - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
% Convert object to number.
%
% ( obj ) ==> ( int )
%
/cvn {
  dup gettype t_string eq {
    1 % sign
    exch dup 0 get '-' eq {
      exch pop 1 add -1 exch
    } if
    10 % initial base
    0 % value
    rot
    {
      dup 'a' ge { 0x20 sub } if
      dup 'X' eq { pop pop pop 16 0 '0' } if
      '0' sub
      dup 9 gt { 7 sub } if
      dup 0 lt over 4 index ge or { pop exit } if
      exch 2 index mul add
    } forall
    exch pop mul
  } {
    t_int settype
  } ifelse
} def


% - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
% Arguments like snprintf.
%
% ( obj_1 ... obj_n string_1 string_2 ) ==> ( )
%
/sprintf {
  dup cvp length exch snprintf
} def


% - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
% Allocate new string.
%
% ( size ) ==> ( string )
/string {
  1 add malloc cvs
} def


% - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
% Increment variable.
%
% ( dict_ref ) ==> ( )
%
/inc {
  dup exec 1 add def
} def


% - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
% Decrement variable.
%
% ( dict_ref ) ==> ( )
%
/dec {
  dup exec 1 sub def
} def


% - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
% Copy src to dst.
%
% Watch overlapping src & dst!
%
% ( dst src ) ==> ( dst )
%
/strcpy {
  "%s" 2 index sprintf
} def


% - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
% Duplicate string.
%
% ( string ) ==> ( string )
%
/strdup {
  dup length string exch strcpy
} def


% - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
% Concatenate strings.
%
% ( string1 string2 ) ==> ( string1 )
%
/strcat {
  over dup length add exch strcpy pop
} def


% - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
% Test for AltGr.
%
% ( )  ==> ( bool )
%
/is_altGr {
  keystat statusAltR and 0 ne keystat statusAltL and 0 eq and
} def


% - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
% Keyboard mapping.
%
% ( key ) ==> ( key )
%
/mapkey {
   dup 24 shr 0xff and /key.code exch def
   is_altGr {
     % bios is too smart...
     key.code 0x78 ge key.code 0x83 le and { /key.code key.code 0x76 sub def } if
   } if
   0 1 config.keymap length 1 sub {
     config.keymap exch get
     dup 0 get key.code eq {
       1
       keystat statusShift and { pop 2 } if
       is_altGr { pop 3 } if
       get
       exch pop
     } {
       pop
     } ifelse
   } for
} def


% - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
% Set password mode font property.
%
% ( font ) ==> ( font )
%
/pwmode {
  dup gettype t_ptr eq { t_int settype } if
} def


% - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
% Test for password mode.
%
% ( font -- true|false )
%
/is.pwmode {
  gettype t_int eq
} def


% - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
% Show one-line string right aligned.
%
% ( string ) ==> ( )
%
/showright1 {
  dup strsize pop neg 0 rmoveto currentpoint rot show currentpoint exch pop exch pop moveto
} def


% - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
% Show string right aligned.
%
% ( string ) ==> ( )
%
/showright {
  '\n' split
  currentpoint pop
  false 2 index

  % array x not_first? array
  {
    % array x not_first? elem

    over {
      "\n" show
      2 index currentpoint exch pop moveto
    } if

    dup showright1 free

    pop true
  } forall
  pop pop
  free
} def


% - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
% Show string right/left aligned.
%
% ( string ) ==> ( )
%
/show.rtl {
  config.rtl { showright } { show } ifelse
} def


% - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
% Show string centered.
%
% ( string ) ==> ( )
%
/showcenter {
  dup strsize pop 2 div neg 0 rmoveto show
} def


% - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
% Generate pseudo random number.
% Good enough for boot loader splash screen.
%
% ( ) ==> ( int )
%
/rand {
  rand.start 59 mul 97 add 0x7fffffff and
  /rand.start over def
} def

% start value
/rand.start time def


% - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
%
% ( date ) ==> ( weekday )
%
% (Monday: 0)
%
% d + [26*(m+1)/10] + j + [j/4] + [c/4] - 2 c - 2
%
/weekday {
  dup day exch
  dup year exch
  month dup 2 le { 12 add exch 1 sub exch } if
  1 add 26 mul 10 div
  exch dup
  100 mod dup 4 div add
  exch 100 div dup 4 div exch 2 mul sub
  add add add
  7 mod 12 add
  7 mod
} def


% - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
%
% ( date ) ==> ( day )
%
/day {
  0xff and
} def


% - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
%
% ( date ) ==> ( month )
%
/month {
  8 shr 0xff and
} def


% - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
%
% ( date ) ==> ( year )
%
/year {
  16 shr
} def


% - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
%
% Read CMOS RAM.
%
% ( index ) ==> ( value )
%
/nvram {
  0x70 exch outbyte
  0x71 inbyte
} def


% - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
% readsector - read sector
%
% group: system
%
% ( int1 -- ptr1 )
%
% int1: sector number
% ptr1: buffer with sector data or .undef. Use @free to free the buffer.
%
% Note: does not return on error. Returns .undef if function is not implemented.
%
/readsector {
  _readsector
  dup .undef eq { return } if

  sectorsize malloc dup rot over length memcpy
} def


% - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
% gfxconfig.data - return array with gfxboot config entries
%
% Reads and parses "gfxboot.cfg" when called first time.
%
% group: system
%
% ( -- array1 )
%
% array1: config values
%
% array1 may be empty but is never .undef.
% Elements of array1 (if any) are arrays of three strings: [ section key value ].
%
/gfxconfig.data {

  % read file and copy to temp string (we need the final 0)
  "gfxboot.cfg" findfile dup .undef ne {
    dup dup length dup string dup cvp 4 2 roll memcpy exch free
  } {
    pop ""
  } ifelse

  % free temp string and create temp array
  /gfxconfig.data over '\n' split def free

  % modifies gfxconfig.data
  /gfxconfig.data [

    "base"	% default section

    gfxconfig.data {

      skipspaces 

      dup 0 get dup 0 eq over ';' eq or exch '#' eq or {
        % empty or comment
        pop
      } {
        dup 0 get '[' eq {
          % [section]
          1 add
          dup "]" strstr dup {
            % put new section on stack
            1 sub over exch 0 put
            exch free
          } {
           % wrong [section] entry 
           pop pop
          } ifelse
        } {
          % key=value?
          dup "=" strstr dup {
            over over 1 sub over exch 0 put add
            [ 3 index 4 2 roll ] exch
          } {
            % no "="
            pop pop
          } ifelse
        } ifelse
      } ifelse
    } forall

    free
  ]

    % free temp array
    gfxconfig.data free

  def

  gfxconfig.data

} def


% - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
% gfxconfig.layout - return array with config file sections
%
% group: system
%
% ( -- array1 )
%
% array1: section names
%
% array1 may be empty but is never .undef.
%
/gfxconfig.layout {

  /gfxconfig.layout [ "base" ] def

  "layout" gfxconfig.array_str
  dup .undef ne {
    gfxconfig.layout free
    [ exch { } forall "base" ] /gfxconfig.layout exch def
  } {
    pop
  } ifelse

  gfxconfig.layout

} def


% - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
% gfxconfig.getentry_s - return gfxboot config file entry
%
% group: system
%
% ( str1 str2 -- str3 )
%
% str1: section
% str2: config entry key
% str3: config value (or .undef)
%
/gfxconfig.getentry_s {
  .undef
  gfxconfig.data {
    3 index over 0 get eq 3 index 2 index 1 get eq and {
      2 get exch pop exit
    } {
      pop
    } ifelse
  } forall

  exch pop exch pop
} def


% - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
% gfxconfig.getentry - return raw gfxboot config file entry
%
% group: system
%
% ( str1 -- str2 )
%
% str1: config entry key
% str2: config value (or .undef)
%
/gfxconfig.getentry {
  .undef

  gfxconfig.layout {
    2 index gfxconfig.getentry_s
    dup .undef eq {
      pop
    } {
      exch pop
      exit
    } ifelse
  } forall

  exch pop
} def


% - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
% gfxconfig.str - return gfxboot config file entry: string
%
% group: system
%
% ( str1 -- str2 )
%
% str1: config entry key
% str2: config value (or .undef)
%
/gfxconfig.str {
  gfxconfig.getentry dup .undef ne { strdup dup dropspaces } if
} def


% - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
% gfxconfig.int - return gfxboot config file entry: integer
%
% group: system
%
% ( str1 -- int1 )
%
% str1: config entry key
% int1: config value (or .undef)
%
/gfxconfig.int {
  gfxconfig.getentry dup .undef ne { cvn } if
} def


% - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
% gfxconfig.int2 - return gfxboot config file entry: two integers
%
% group: system
%
% ( str1 -- int1 int2 )
%
% str1: config entry key
% int1: first config value (or .undef)
% int2: second config value (or .undef)
%
/gfxconfig.int2 {
  gfxconfig.array_int dup .undef eq { pop [ ] } if
  dup 0 aget over 1 aget rot free
} def


% - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
% gfxconfig.array_str - return gfxboot config file entry: array of strings
%
% group: system
%
% ( str1 -- array1 )
%
% str1: config entry key
% array1: config value (or .undef)
%
/gfxconfig.array_str {
  gfxconfig.getentry dup .undef ne { ',' split } if
} def


% - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
% gfxconfig.array_int - return gfxboot config file entry: array of integers
%
% group: system
%
% ( str1 -- array1 )
%
% str1: config entry key
% array1: config value (or .undef)
%
/gfxconfig.array_int {
  gfxconfig.array_str dup .undef ne {
    [ exch
      { dup .undef ne { cvn } if } forall
    ]
  } if
} def


% - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
% gfxconfig.set.str - initialize variable with gfxboot config file entry
%
% group: system
%
% ( dict1 str1 str2 -- )
%
% dict1: variable to modify
% str1: config entry key
% str2: default value
%
/gfxconfig.set.str {
  exch gfxconfig.str
  dup .undef ne { exch } if pop def
} def


% - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
% gfxconfig.set.int - initialize variable with gfxboot config file entry
%
% group: system
%
% ( dict1 str1 int1 -- )
%
% dict1: variable to modify
% str1: config entry key
% int1: default value
%
/gfxconfig.set.int {
  exch gfxconfig.int
  dup .undef ne { exch } if pop def
} def


% - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
% gfxconfig.set.int2 - initialize two variables with gfxboot config file entry
%
% group: system
%
% ( dict1 dict2 str1 int1 int2 -- )
%
% dict1: first variable to modify
% dict2: second variable to modify
% str1: config entry key
% int1: first default value
% int2: second default value
%
/gfxconfig.set.int2 {
  rot gfxconfig.int2
  % dict1 dict2 def1 def2 val1 val2
  exch 4 -1 roll exch
  % dict1 dict2 def2 val2 def1 val1
  dup .undef ne { exch } if pop
  5 -1 roll exch def
  % dict2 def2 val2
  dup .undef ne { exch } if pop def
} def


% - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
% gfxconfig.set.bool - initialize variable with gfxboot config file entry
%
% group: system
%
% ( dict1 str1 bool1 -- )
%
% dict1: variable to modify
% str1: config entry key
% bool1: default value
%
/gfxconfig.set.bool {
  exch gfxconfig.int
  dup .undef ne { 0 ne exch } if pop def
} def


% - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
% gfxconfig.set.array_str - initialize variable with gfxboot config file entry
%
% group: system
%
% ( dict1 str1 array1 -- )
%
% dict1: variable to modify
% str1: config entry key
% array1: default value
%
/gfxconfig.set.array_str {
  exch gfxconfig.array_str
  dup .undef ne { exch } if pop def
} def


% - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
% gfxconfig.set.array_int - initialize variable with gfxboot config file entry
%
% group: system
%
% ( dict1 str1 array1 -- )
%
% dict1: variable to modify
% str1: config entry key
% array1: default value
%
/gfxconfig.set.array_int {
  exch gfxconfig.array_int
  dup .undef ne { exch } if pop def
} def


% - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
% split - split string
%
% group:
%
% ( str1 int1 -- array1 )
%
% str1: string
% int1: char
% array1: array of strings
%
/split {

  % split does not work if str1 is in a special memory region (where 
  % 'cvp length' does not work). So we dup it first.

  exch strdup dup rot

  currenteotchar exch seteotchar exch

  [ exch

    {
      dup strdup exch
      dup length add

      dup cvp length 1 le { pop exit } if

      1 add

    } loop

  ]

  exch seteotchar

  exch free

} def


% - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
% join - join array of strings
%
% group:
%
% ( array1 str1 -- str2 )
%
% array1: array of strings
% str1: separator
% str2: complete string
%
/join {
  over length 0 eq { pop pop 0 string return } if

  over length 1 sub over length mul
  2 index { length add } forall
  string

  % note: last element is not followed by separator because it exceeds
  % the destination string size
  rot {
    strcat over strcat
  } forall

  exch pop

} def


% - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
% Skip leading non-spaces.
%
% ( string ) ==> ( string )
%
/skipnonspaces {
  { dup 0 get dup 0 ne exch ' ' ne and { 1 add } { exit } ifelse } loop
} def


% - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
% Skip leading spaces.
%
% ( string ) ==> ( string )
%
/skipspaces {
  { dup 0 get ' ' eq { 1 add } { exit } ifelse } loop
} def


% - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
% Drop spaces at string end.
% Modifies string!
%
% ( string ) ==> ( )
%
/dropspaces {
  dup length
  dup 0 eq {
    pop pop
  } {
    1 sub
    -1 0 {
      over over get ' ' eq { over exch 0 put } { pop exit } ifelse
    } for
    pop
  } ifelse
} def


% - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
% Test if string[ofs-1]/string[ofs] is a word boundary.
%
% ( string ofs ) ==> ( true|false )
%
% boundary is either space/non-space or non-space/(space|'=')
%
/iswordboundary {
  dup 0 eq { pop pop true return } if

  add dup 1 sub 0 get exch 0 get

  over ' ' eq over ' ' gt and { pop pop true return } if
  over ' ' gt over dup ' ' eq exch dup '=' eq exch 0 eq or or and { pop pop true return } if

  pop pop false
} def


%% findmode - find video mode number
%
% group: gfx.screen
%
% ( int1 int2 int3 -- int4 )   
%
% int1, int2: width, height
% int3: color bits
% int4: mode number (or .undef)
%
% example
%   1024 768 16 findmode setmode        % 1024x768, 16-bit color mode
%
/findmode {
  0 1 videomodes {
    videomodeinfo dup .undef eq {
      pop pop pop pop
    } {
      % compare width, height, colors
      6 index 4 index eq 6 index 4 index eq and 5 index 3 index eq and {
        7 1 roll 6 { pop } repeat 0xbfff and return
      } {
        pop pop pop pop
      } ifelse
    } ifelse
  } for

  pop pop pop .undef
} def


% - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
%
% Replace substring. Returns newly allocated string.
%
% ( str key value ) ==> ( new_str )
%
% Replaces first occurence of 'key' in str with 'value'.
%
/strreplace {
  2 index 2 index strstr dup 0 ne {
    1 sub
    over length 3 index length sub 4 index length add string
    dup cvp 5 index cvp 3 index memcpy
    dup 6 1 roll over add exch 5 -1 roll exch add
    4 -1 roll length add 3 1 roll "%s%s" exch sprintf
  } {
    pop pop pop strdup
  } ifelse


} def


% - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
% Search for option in cmdline.
% Returns .undef if not found.
%
% ( cmdline option_name ) ==> ( option_start )
%
/bootopt.find {
  /_bo.opt exch def
  /_bo.cmdline exch def
  /_bo.= _bo.opt dup length 1 sub get '=' eq def

  {
    _bo.cmdline _bo.opt strstr
    dup {
      dup 1 eq {
        true
      } {
        dup 2 sub _bo.cmdline exch get ' ' eq
      } ifelse

      {
        _bo.cmdline over _bo.opt length add 1 sub get
        dup '=' eq
        over ' ' eq or
        exch 0 eq or
        _bo.= or
      } {
        false
      } ifelse

      _bo.cmdline rot add exch

      {
        1 sub exit
      } {
        /_bo.cmdline exch def
      } ifelse
    } {
      pop
      .undef exit
    } ifelse
  } loop

} def


% - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
% Search for second occurence of option in cmdline.
% Returns .undef if not found.
%
% ( cmdline option_name ) ==> ( option_start )
%
/bootopt.find2 {
  over over bootopt.find dup .undef eq {
    pop pop pop .undef
  } {
    1 add rot pop exch bootopt.find
  } ifelse
} def


% Remove option from cmdline. Returns removed option or .undef.
%
% cmdline is modified, option_entry is allocated dynamicyll and must be
% freed later.
%
% ( cmdline option_name -- option_entry )
%
/bootopt.remove {
  bootopt.find dup .undef ne {
    dup
    skipnonspaces dup skipspaces 2 index sub rot rot over sub string over strcpy
    rot rot
    {
      over over exch get
      over over 0 exch put {
        1 add
      } {
        exit
      } ifelse
    } loop
    pop pop
  } if
} def


% Video memory in kb.
%
% (  -- int )
%
/video.memory {
  /video.memory 0 sysinfo def
  video.memory
} def


% Graphics card OEM info.
%
% (  -- string )
%
/video.oem {
  /video.oem 1 sysinfo strdup def
  video.oem
} def


% Graphics card vendor name.
%
% (  -- string )
%
/video.vendor {
  /video.vendor 2 sysinfo strdup def
  video.vendor
} def


% Graphics card product name.
%
% (  -- string )
%
/video.product {
  /video.product 3 sysinfo strdup def
  video.product
} def


% Graphics card revision.
%
% (  -- string )
%
/video.revision {
  /video.revision 4 sysinfo strdup def
  video.revision
} def


% - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
% Free memory.
%
% Like free, but accepts dict entries, too.
%
% ( obj -- )
%
/xfree {
  dup gettype
  t_dict_idx eq {
    dup exec exch .undef def
  } if
  free
} def


% - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
% Free array and all its elements.
%
% If array is a dict entries, undefines it, too.
%
% ( array -- )
%
/afree {
  dup .undef ne {
    dup gettype t_dict_idx eq { dup exec exch .undef def } if
    dup { free } forall
    free
  } {
    pop
  } ifelse
} def


% - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
% Get array element.
%
% Like get, but returns .undef if index is outside array bounds.
%
% ( array index -- obj )
%
/aget {
  over length over gt { get } { pop pop .undef } ifelse
} def


% - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
% Search for array element.
%
% ( array key -- bool )
%
/iselement {
  false rot {
    2 index eq { pop true exit } if
  } forall
  exch pop
} def


% - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
% pc speaker beep.
%
% ( freq duration -- )
%
% freq in Hz, duration in microseconds.
%
/beep {
  exch
  0x61 inbyte
  dup 3 or 0x61 exch outbyte
  0x43 0xb6 outbyte
  exch 2386360 exch div

  dup 0x42 exch outbyte
  8 shr 0x42 exch outbyte

  exch usleep

  0x61 exch outbyte
} def


% - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
% Play movie.
%
% ( str1 -- )
%
% Image filename template.
%
/play_movie {
  64 string

  0

  {
    1 add
    dup 3 index 3 index sprintf

    over findfile dup .undef eq {
      pop exit
    } {
      10000 usleep
      currentimage
      over setimage 0 0 image.size image
      setimage
      free
    } ifelse


  } loop

  pop free pop

} def


