Class: Remind::Native

Inherits:
Object
  • Object
show all
Defined in:
lib/remind/native.rb

Overview

The dlopen'd library: the Remind functions the bindings call, the shim functions that reach what Fiddle cannot, and the globals Remind keeps its state in.

Fiddle rather than a C extension, because there is no extension to write. Remind's sources build into a shared library as they are, and everything here is a plain C function over ints and pointers. The one file of C in this gem, ext/shim.c, exists for struct layout and for the six-call sequence that parses a REM line -- not because Fiddle needs help calling anything.

Functions are looked up on first call and kept: Fiddle::Handle#[] is a dlsym every time.

Constant Summary collapse

INT =
Fiddle::TYPE_INT
POINTER =
Fiddle::TYPE_VOIDP
SIZE =
Fiddle::TYPE_SIZE_T
VOID =
Fiddle::TYPE_VOID
FUNCTIONS =

name => [C symbol, argument types, return type]

{
  # Dates. Remind counts days from 1990-01-01 and numbers months from
  # zero; both conversions are called rather than repeated, so the epoch
  # cannot drift from the library's.
  dse:                    ["DSE", [INT, INT, INT], INT],
  from_dse:               ["FromDSE", [INT, POINTER, POINTER, POINTER], VOID],

  # Expressions, and the messages for when they fail.
  evaluate:               ["EvalExpr", [POINTER, POINTER, POINTER], INT],
  error_message:          ["GetErr", [INT], POINTER],

  # The startup sequence `Runtime` mirrors. Remind does all of this inside
  # InitRemind, which also parses argv, opens files and exits on bad
  # input, and is therefore not something a library can call.
  init_variables:         ["InitVars", [], VOID],
  init_user_functions:    ["InitUserFunctions", [], VOID],
  init_translation_table: ["InitTranslationTable", [], VOID],
  init_files:             ["InitFiles", [], VOID],
  init_dedupe_table:      ["InitDedupeTable", [], VOID],
  init_buffer:            ["DBufInit", [POINTER], VOID],
  system_date:            ["SystemDate", [POINTER, POINTER, POINTER], INT],
  system_time:            ["SystemTime", [INT], INT],

  # libc's, through the same handle: Remind writes its diagnostics through
  # a FILE *, so somewhere to point it is part of the interface.
  open_stream:            ["fopen", [POINTER, POINTER], POINTER],

  # The shim.
  sizeof_trigger:         ["remrb_sizeof_trigger", [], SIZE],
  sizeof_timetrig:        ["remrb_sizeof_timetrig", [], SIZE],
  parse_reminder:         ["remrb_parse_reminder", [POINTER, POINTER, POINTER, POINTER, POINTER, INT], INT],
  next_trigger:           ["remrb_next_trigger", [POINTER, POINTER, INT, POINTER], INT],
  free_trigger:           ["remrb_free_trigger", [POINTER], VOID],
  open_file:              ["remrb_open_file", [POINTER], INT],
  read_line:              ["remrb_read_line", [POINTER], INT],
  trigger_tags:           ["remrb_trigger_tags", [POINTER], POINTER],
  trigger_timezone:       ["remrb_trigger_timezone", [POINTER], POINTER],
}.freeze
TRIGGER_FIELDS =

The Trigger fields a calendar entry is made out of. Each is an accessor in the shim, compiled against Remind's own headers, so no byte offset is ever written down on this side.

%i[
  wd d m y back delta rep localomit skip until typ once scanfrom from
  priority duration_days eventduration eventstart is_todo addomit need_wkday
].freeze
TIMETRIG_FIELDS =
%i[time delta repeat duration].freeze
CONSTANTS =

The values Remind uses for "the reminder did not say", and the constants it tags a body with. Read from the shim rather than copied, for the same reason as the field offsets.

%i[
  no_day no_month no_year no_weekday no_until no_time quote_marker
  normal_mode cal_mode eof
  msg_type msf_type run_type cal_type sat_type passthru_type
].freeze
GLOBALS =

Remind keeps its idea of now in globals rather than passing it around, so these are as much a part of the interface as the functions.

DSEToday       the date every relative calculation is relative to
LocalDSEToday  the same, before a --date override moves it
RealToday      what the system clock said at startup
CurYear        the year an incomplete date is completed with
SysTime        seconds since midnight, or -1 to read the clock
%w[DSEToday LocalDSEToday RealToday CurYear SysTime LocalSysTime].freeze
BUFFERS =

The dynamic buffers Remind expects to have been initialised, and the stream it expects to have been pointed somewhere.

%w[Banner LineBuffer ExprBuf].freeze
ERROR_STREAM =
"ErrFp"

Instance Attribute Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(path = Library.path) ⇒ Native

Returns a new instance of Native.



107
108
109
110
111
# File 'lib/remind/native.rb', line 107

def initialize(path = Library.path)
  @path = path
  @handle = Fiddle::Handle.new(path)
  @functions = {}
end

Instance Attribute Details

#pathObject (readonly)

Returns the value of attribute path.



105
106
107
# File 'lib/remind/native.rb', line 105

def path
  @path
end

Instance Method Details

#address_of(name) ⇒ Object

The address of a global, for the functions that take one: DBufInit wants the buffer itself, not a copy of it.



147
148
149
# File 'lib/remind/native.rb', line 147

def address_of(name)
  Fiddle::Pointer.new(@handle[name])
end

#read_global(name) ⇒ Object



137
138
139
# File 'lib/remind/native.rb', line 137

def read_global(name)
  global(name)[0, Fiddle::SIZEOF_INT].unpack1("i!")
end

#standard_errorObject

stderr is a variable in libc, and it holds the FILE * rather than being one.



159
160
161
162
163
164
# File 'lib/remind/native.rb', line 159

def standard_error
  Fiddle::Pointer.new(Fiddle::Handle::DEFAULT["stderr"], Fiddle::SIZEOF_VOIDP)[
    0,
    Fiddle::SIZEOF_VOIDP,
  ].unpack1("J")
end

#write_global(name, value) ⇒ Object



141
142
143
# File 'lib/remind/native.rb', line 141

def write_global(name, value)
  global(name)[0, Fiddle::SIZEOF_INT] = [value].pack("i!")
end

#write_pointer(name, address) ⇒ Object

A pointer-sized global, which is what a FILE * is.



152
153
154
155
# File 'lib/remind/native.rb', line 152

def write_pointer(name, address)
  Fiddle::Pointer.new(@handle[name], Fiddle::SIZEOF_VOIDP)[0, Fiddle::SIZEOF_VOIDP] =
    [address].pack("J")
end