Module: Remind::Runtime

Defined in:
lib/remind/runtime.rb

Overview

The startup Remind does before it looks at its command line.

InitRemind is one function with two jobs: it brings the interpreter up -- hash tables, dynamic buffers, the translation table, today's date -- and then it parses argv, opens files and, on bad input, exits the process. A library wants the first job and never the second, so this repeats the first rather than calling the function that does both.

Skipping it is not an option. Remind's tables are static storage with null function pointers in them until something initialises them, so the first expression that takes an error path -- where GetErr translates the message -- walks into a null pointer and takes the process with it.

It runs once per process, because the state is process-wide.

Constant Summary collapse

LOCK =
Mutex.new
TABLES =

In the order InitRemind does them.

%i[
  init_variables
  init_user_functions
  init_translation_table
  init_files
  init_dedupe_table
].freeze
DEVNULL =

Remind reports a parse error by printing the line with a caret under the offending character. A library raises instead, so by default the printing goes nowhere and the exception carries the message; a caller who wants the caret can ask for it.

"/dev/null"

Class Method Summary collapse

Class Method Details

.boot(native, diagnostics: false) ⇒ Object



44
45
46
47
48
49
50
51
52
# File 'lib/remind/runtime.rb', line 44

def boot(native, diagnostics: false)
  LOCK.synchronize do
    if booted[native.path]
      false
    else
      start(native, diagnostics)
    end
  end
end

.bootedObject



40
41
42
# File 'lib/remind/runtime.rb', line 40

def booted
  @booted ||= {}
end

.discard(native) ⇒ Object

One stream per process, held open: reopening it per expression would leak a descriptor every time.



76
77
78
79
80
81
# File 'lib/remind/runtime.rb', line 76

def discard(native)
  @discard ||= native.open_stream(
    Fiddle::Pointer["#{DEVNULL}\0"],
    Fiddle::Pointer["w\0"],
  ).to_i
end

.point_errors(native, diagnostics) ⇒ Object

ErrFp starts as a null FILE *, and main() is what points it at stderr. Everything that reports an error writes through it, including the paths that would otherwise dereference the null.



66
67
68
69
70
71
72
# File 'lib/remind/runtime.rb', line 66

def point_errors(native, diagnostics)
  if diagnostics
    native.write_pointer(Native::ERROR_STREAM, native.standard_error)
  else
    native.write_pointer(Native::ERROR_STREAM, discard(native))
  end
end

.read_the_clock(native) ⇒ Object

SystemDate answers today as a day count and fills in the year, month and day Remind completes partial dates with.



85
86
87
88
89
90
91
92
93
94
95
96
# File 'lib/remind/runtime.rb', line 85

def read_the_clock(native)
  year = Fiddle::Pointer.malloc(Fiddle::SIZEOF_INT, Fiddle::RUBY_FREE)
  month = Fiddle::Pointer.malloc(Fiddle::SIZEOF_INT, Fiddle::RUBY_FREE)
  day = Fiddle::Pointer.malloc(Fiddle::SIZEOF_INT, Fiddle::RUBY_FREE)
  today = native.system_date(year, month, day)

  native.write_global("RealToday", today)
  native.write_global("DSEToday", today)
  native.write_global("LocalDSEToday", today)
  native.write_global("CurYear", year[0, Fiddle::SIZEOF_INT].unpack1("i!"))
  native.write_global("LocalSysTime", native.system_time(0))
end

.start(native, diagnostics) ⇒ Object



54
55
56
57
58
59
60
61
# File 'lib/remind/runtime.rb', line 54

def start(native, diagnostics)
  point_errors(native, diagnostics)
  TABLES.each { |name| native.public_send(name) }
  Native::BUFFERS.each { |name| native.init_buffer(native.address_of(name)) }
  read_the_clock(native)

  booted[native.path] = true
end