dgd-tools 0.1.2 → 0.1.7

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,12 @@
1
+ {
2
+ "name": "ChatTheatre Kernellib",
3
+ "git": {
4
+ "url": "https://github.com/ChatTheatre/kernellib.git",
5
+ "branch": "master"
6
+ },
7
+ "paths": {
8
+ "src/include/kernel": "include/kernel",
9
+ "src/include/*.h": "include",
10
+ "src/kernel": "kernel"
11
+ }
12
+ }
@@ -16,7 +16,6 @@ module DGD::Manifest
16
16
  }
17
17
  KERNEL_PATHS = KERNEL_PATH_MAP.values
18
18
  DEFAULT_KERNELLIB_URL = "https://github.com/ChatTheatre/kernellib"
19
-
20
19
  GENERATED_ROOT = ".root"
21
20
 
22
21
  def self.system_call(cmd)
@@ -30,24 +29,26 @@ module DGD::Manifest
30
29
  # This is a repo of everything DGD Manifest saves between runs.
31
30
  # It includes downloaded Git repos, Goods files and more.
32
31
  class Repo
33
- attr_reader :manifest_dir
32
+ attr_reader :shared_dir
34
33
 
35
34
  def initialize
35
+ @no_manifest_file = true
36
36
  @home = ENV["HOME"]
37
- @manifest_dir = "#{@home}/.dgd-tools"
38
- Dir.mkdir(@manifest_dir) unless File.directory?(@manifest_dir)
37
+ @shared_dir = "#{@home}/.dgd-tools"
38
+ Dir.mkdir(@shared_dir) unless File.directory?(@shared_dir)
39
+
39
40
  ["git", "goods"].each do |subdir|
40
- full_subdir = "#{@manifest_dir}/#{subdir}"
41
+ full_subdir = "#{@shared_dir}/#{subdir}"
41
42
  Dir.mkdir(full_subdir) unless File.directory?(full_subdir)
42
43
  end
43
44
 
44
- unless File.exist?("#{@manifest_dir}/dgd/bin/dgd")
45
- dgd_dir = "#{@manifest_dir}/dgd"
45
+ unless File.exist?("#{@shared_dir}/dgd/bin/dgd")
46
+ dgd_dir = "#{@shared_dir}/dgd"
46
47
  if File.directory?(dgd_dir)
47
48
  # Not clear to me what to do here...
48
49
  else
49
50
  DGD::Manifest.system_call("git clone https://github.com/ChatTheatre/dgd.git #{dgd_dir}")
50
- Dir.chdir("#{@manifest_dir}/dgd/src") do
51
+ Dir.chdir("#{@shared_dir}/dgd/src") do
51
52
  DGD::Manifest.system_call(DGD_BUILD_COMMAND)
52
53
  end
53
54
  end
@@ -60,77 +61,120 @@ module DGD::Manifest
60
61
  end
61
62
 
62
63
  def manifest_file(path)
63
- raise "Already have a dgd.manifest file!" if @manifest_file
64
+ raise "Already have a dgd.manifest file!" unless @no_manifest_file
64
65
 
66
+ @no_manifest_file = false
65
67
  @manifest_file ||= AppFile.new(self, path)
66
68
  end
67
69
 
68
- def assemble_app(location)
69
- dgd_root = "#{File.expand_path(location)}/#{GENERATED_ROOT}"
70
- app_path = "#{File.expand_path(location)}/#{@manifest_file.app_root}"
71
- FileUtils.rm_rf(dgd_root)
72
- FileUtils.cp_r(app_path, dgd_root)
70
+ protected
73
71
 
74
- write_config_file("#{location}/dgd.config")
75
- specs = @manifest_file.specs
72
+ # This includes files to assemble... But also subdirectories and commands. This format is
73
+ # unstable and ugly, and should not be exposed to outside parties who might later depend on it.
74
+ def assembly_operations(location)
75
+ operations = []
76
76
 
77
- copies = []
77
+ raise("No manifest file!") if @no_manifest_file
78
78
 
79
- specs.each do |spec|
80
- git_repo = spec.source
81
- git_repo.use_details(spec.source_details)
79
+ @manifest_file.specs.each do |spec|
80
+ spec_git_repo = spec.source
81
+ spec_git_repo.use_details(spec.source_details) # This sets things like checked-out branch
82
82
 
83
83
  spec.paths.each do |from, to|
84
- from_path = "#{git_repo.local_dir}/#{from}"
85
- to_path = "#{dgd_root}/#{to}"
86
- copies << [from_path, to_path]
84
+ # Note: spec_git_repo.local_dir is an absolute path.
85
+ from_path = "#{spec_git_repo.local_dir}/#{from}"
86
+ if File.directory?(from_path)
87
+ files = Dir["#{from_path}/**/*"].to_a + Dir["#{from_path}/**/.*"].to_a
88
+ dirs = files.select { |file| File.directory?(file) }
89
+ non_dirs = files - dirs
90
+ operations << { cmd: "cp", from: from_path, to: to, dirs: dirs, non_dirs: non_dirs, comment: :single_dir }
91
+ elsif from_path["*"] # If from_path contains at least one asterisk
92
+ components = from.split("/")
93
+ first_wild_idx = components.index { |item| item["*"] }
94
+ no_wild_from_path = components[0..(first_wild_idx-1)].join("/")
95
+ wild_path = components[first_wild_idx..-1].join("/")
96
+
97
+ files = Dir["#{spec_git_repo.local_dir}/#{no_wild_from_path}/#{wild_path}"].to_a
98
+ dirs = files.select { |file| File.directory?(file) }
99
+ dirs += files.map { |f| File.dirname(f) }
100
+ dirs.uniq!
101
+
102
+ non_dirs = files - dirs
103
+ operations << { cmd: "cp", from: "#{spec_git_repo.local_dir}/#{no_wild_from_path}", to: to, dirs: dirs, non_dirs: non_dirs, comment: :path_wildcard }
104
+ else
105
+ # A single file
106
+ operations << { cmd: "cp", from: from_path, to: to, dirs: [], non_dirs: [from_path], comment: :single_file }
107
+ end
108
+ end
109
+ end
110
+
111
+ app_path = "#{File.expand_path(location)}/#{@manifest_file.app_root}"
112
+ app_files = Dir["#{app_path}/**/*"].to_a
113
+ app_dirs = app_files.select { |f| File.directory?(f) }
114
+ app_non_dirs = app_files - app_dirs
115
+ unless app_dirs.empty? && app_non_dirs.empty?
116
+ operations << { cmd: "cp", from: app_path, to: ".", dirs: app_dirs, non_dirs: app_non_dirs, comment: :app_files } # No source
117
+ end
118
+
119
+ operations
120
+ end
121
+
122
+ public
123
+
124
+ def dgd_root(location)
125
+ "#{File.expand_path(location)}/#{GENERATED_ROOT}"
126
+ end
127
+
128
+ def assemble_app(location)
129
+ Dir[File.join(dgd_root(location), "*")].each { |dir| FileUtils.rm_rf dir }
130
+
131
+ write_app_files(location)
132
+ end
133
+
134
+ def update_app(location)
135
+ write_app_files(location) # TODO: maybe check files dates? Some way to do update-only
136
+ end
137
+
138
+ protected
139
+
140
+ def write_app_files(location)
141
+ Dir.chdir(location) do
142
+ write_config_file("#{location}/dgd.config")
143
+ FileUtils.mkdir_p("#{location}/state") # Statedir for statedumps, editor files, etc.
144
+
145
+ assembly_operations(location).each do |sd_hash|
146
+ to_path = "#{dgd_root(location)}/#{sd_hash[:to]}"
147
+
148
+ # Make appropriate dirs, including empty ones
149
+ sd_hash[:dirs].each do |dir|
150
+ FileUtils.mkdir_p dir.sub(sd_hash[:from], to_path)
151
+ end
152
+
153
+ # Copy all files
154
+ sd_hash[:non_dirs].each do |from_file|
155
+ to_file = from_file.sub(sd_hash[:from], "#{dgd_root(location)}/#{sd_hash[:to]}")
156
+ to_dir = File.dirname(to_file)
157
+ FileUtils.mkdir_p to_dir
158
+ FileUtils.cp from_file, to_file
159
+ end
87
160
  end
88
161
  end
162
+ end
89
163
 
90
- copies.sort_by { |c| c[1] }.each do |from_path, to_path|
91
- to_dir = to_path.split("/")[0..-2].join("/")
92
- FileUtils.mkdir_p to_dir
93
- STDERR.puts "COPYING #{from_path.inspect} #{to_path.inspect}"
94
- FileUtils.cp_r(from_path, to_path)
164
+ public
165
+
166
+ def precheck(location)
167
+ all_files = assembly_operations(location).flat_map { |sd| sd[:non_dirs] }
168
+
169
+ if all_files.size != all_files.uniq.size
170
+ repeated = all_files.uniq.select { |f| all_files.count(f) > 1 }
171
+ raise "Error in dgd.manifest! Repeated files: #{repeated.inspect} / #{all_files.inspect}"
95
172
  end
96
173
  end
97
174
 
98
175
  def write_config_file(path)
99
176
  File.open(path, "wb") do |f|
100
- f.write <<CONTENTS
101
- /* These are SkotOS limits. They are enormous. They should
102
- be configurable but they are not yet. */
103
- telnet_port = ([
104
- "*":50100 /* telnet port number */
105
- ]);
106
- binary_port = ([
107
- "*":50110 /* Failsafe */
108
- ]); /* binary ports */
109
- directory = "./#{GENERATED_ROOT}";
110
-
111
- users = 100; /* max # of users */
112
- editors = 40; /* max # of editor sessions */
113
- ed_tmpfile = "../state/ed"; /* proto editor tmpfile */
114
- swap_file = "../state/swap"; /* swap file */
115
- swap_size = 1048576; /* # sectors in swap file */
116
- sector_size = 512; /* swap sector size */
117
- swap_fragment = 4096; /* fragment to swap out */
118
- static_chunk = 64512; /* static memory chunk */
119
- dynamic_chunk = 261120; /* dynamic memory chunk */
120
- dump_file = "../state/dump"; /* dump file */
121
- dump_interval = 3600; /* dump interval */
122
-
123
- typechecking = 2; /* highest level of typechecking */
124
- include_file = "/include/std.h"; /* standard include file */
125
- include_dirs = ({ "/include", "~/include" }); /* directories to search */
126
- auto_object = "/kernel/lib/auto"; /* auto inherited object */
127
- driver_object = "/kernel/sys/driver"; /* driver object */
128
- create = "_F_create"; /* name of create function */
129
-
130
- array_size = 16384; /* max array size */
131
- objects = 262144; /* max # of objects */
132
- call_outs = 16384; /* max # of call_outs */
133
- CONTENTS
177
+ f.write @manifest_file.dgd_config.as_file
134
178
  end
135
179
  end
136
180
  end
@@ -145,7 +189,7 @@ CONTENTS
145
189
  @git_url = git_url
146
190
  @repo = repo
147
191
  local_path = git_url.tr("/\\", "_")
148
- @local_dir = "#{@repo.manifest_dir}/git/#{local_path}"
192
+ @local_dir = "#{@repo.shared_dir}/git/#{local_path}"
149
193
 
150
194
  if File.directory?(@local_dir)
151
195
  Dir.chdir(@local_dir) do
@@ -157,19 +201,17 @@ CONTENTS
157
201
  end
158
202
 
159
203
  def default_branch
160
- return @default_branch if @default_branch
161
- output = `git rev-parse --abbrev-ref origin/HEAD`.chomp
162
- @default_branch = output.gsub(/^origin\//, "")
204
+ @default_branch ||= `git rev-parse --abbrev-ref origin/HEAD`.chomp.gsub(/^origin\//, "")
163
205
  end
164
206
 
165
207
  def use_details(details)
166
208
  if details["branch"]
167
209
  Dir.chdir(@local_dir) do
168
- DGD::Manifest.system_call("git checkout #{details["branch"]}")
210
+ DGD::Manifest.system_call("git checkout #{details["branch"]} && git pull")
169
211
  end
170
212
  else
171
213
  Dir.chdir(@local_dir) do
172
- DGD::Manifest.system_call("git checkout #{default_branch}")
214
+ DGD::Manifest.system_call("git checkout #{default_branch} && git pull")
173
215
  end
174
216
  end
175
217
  end
@@ -179,47 +221,64 @@ CONTENTS
179
221
  attr_reader :path
180
222
  attr_reader :repo
181
223
  attr_reader :specs
182
- attr_reader :app_root
224
+ attr_reader :dgd_config
183
225
 
184
226
  def initialize(repo, path)
185
227
  @path = path
186
228
  @repo = repo
187
229
  raise("No such dgd.manifest file as #{path.inspect}!") unless File.exist?(path)
188
- contents = JSON.load(File.read(path))
230
+ contents = AppFile.parse_manifest_file(path)
189
231
 
190
232
  read_manifest_file(contents)
191
233
 
192
- @app_root = contents["app_root"] || "app"
193
-
194
234
  output_paths = @specs.flat_map { |s| s.paths.values }
195
235
  unless output_paths == output_paths.uniq
196
236
  repeated_paths = output_paths.select { |p| output_paths.count(p) > 1 }
197
237
  raise "Repeated (conflicting?) paths in dgd.manifest! #{repeated_paths.inspect}"
198
238
  end
199
239
 
200
- # Make sure the dgd.manifest file overrides either no kernel paths or both/all
201
- if KERNEL_PATHS.any? { |kp| output_paths.include?(kp) }
202
- unless KERNEL_PATHS.all? { |kp| output_paths.include?(kp) }
203
- raise "dgd.manifest file #{path.inspect} includes some Kernel Library paths but not all! All needed: #{KERNEL_PATHS}!"
204
- end
205
- puts "This dgd.manifest file overrides the Kernel Library with its own."
206
- else
207
- puts "This dgd.manifest needs the default Kernel Library."
208
- # This app has specified no kernellib paths -- add them
209
- git_repo = @repo.git_repo(DEFAULT_KERNELLIB_URL)
210
- klib_spec = GoodsSpec.new @repo, name: "default Kernel Library",
211
- source: git_repo, paths: KERNEL_PATH_MAP
212
- specs.unshift klib_spec
213
- end
240
+ ## Make sure the dgd.manifest file overrides either no kernel paths or both/all
241
+ #if KERNEL_PATHS.any? { |kp| output_paths.include?(kp) }
242
+ # unless KERNEL_PATHS.all? { |kp| output_paths.include?(kp) }
243
+ # raise "dgd.manifest file #{path.inspect} includes some Kernel Library paths but not all! All needed: #{KERNEL_PATHS}!"
244
+ # end
245
+ # puts "This dgd.manifest file overrides the Kernel Library with its own."
246
+ #else
247
+ # puts "This dgd.manifest needs the default Kernel Library."
248
+ # # This app has specified no kernellib paths -- add them
249
+ # spec_git_repo = @repo.git_repo(DEFAULT_KERNELLIB_URL)
250
+ # klib_spec = GoodsSpec.new @repo, name: "default Kernel Library",
251
+ # source: spec_git_repo, paths: KERNEL_PATH_MAP
252
+ # specs.unshift klib_spec
253
+ #end
214
254
 
215
255
  nil
216
256
  end
217
257
 
258
+ # Load the JSON and then remove comments
259
+ def self.parse_manifest_file(path)
260
+ contents = JSON.parse(File.read path)
261
+ remove_comments!(contents)
262
+ contents
263
+ end
264
+
265
+ def self.remove_comments!(items)
266
+ if items.is_a?(Hash)
267
+ items.delete_if { |k, v| k[0] == "#" }
268
+ items.values.each { |v| remove_comments!(v) }
269
+ elsif items.is_a?(Array)
270
+ items.delete_if { |i| i.is_a?(String) && i[0] == "#" }
271
+ items.each { |i| remove_comments!(i) }
272
+ end
273
+ end
274
+
218
275
  def read_manifest_file(contents)
219
276
  raise "Expected a top-level JSON object in dgd.manifest!" unless contents.is_a?(Hash)
220
277
 
221
278
  @specs = []
222
279
 
280
+ @dgd_config = DGDRuntimeConfig.new (contents["config"] || {})
281
+
223
282
  if contents["unbundled_goods"]
224
283
  raise "Unbundled_goods must be an array!" unless contents["unbundled_goods"].is_a?(Array)
225
284
 
@@ -241,9 +300,15 @@ CONTENTS
241
300
  end
242
301
  end
243
302
 
303
+ def app_root
304
+ @dgd_config.app_root
305
+ end
306
+
244
307
  def unbundled_json_to_spec(fields)
245
308
  source = nil
246
309
  source_details = nil
310
+ dependencies = []
311
+
247
312
  if fields["git"]
248
313
  raise "A git source requires a git url: #{fields.inspect}!" unless fields["git"]["url"]
249
314
  source = @repo.git_repo(fields["git"]["url"])
@@ -256,7 +321,23 @@ CONTENTS
256
321
  raise "Paths in Goods files must map strings to strings! #{fields["paths"].inspect}"
257
322
  end
258
323
 
259
- spec = GoodsSpec.new(@repo, name: fields["name"], source: source, source_details: source_details, paths: fields["paths"])
324
+ if fields["dependencies"]
325
+ # For now, permit a single string as a dependency.
326
+ fields["dependencies"] = [ fields["dependencies"] ] if fields["dependencies"].is_a?(String)
327
+
328
+ fields["dependencies"].each do |dep|
329
+ if dep.is_a?(String)
330
+ dependencies.push "url" => dep
331
+ elsif dep.is_a?(Hash)
332
+ raise "Currently only URL-based dependencies on Goods files are supported!" unless dep["url"]
333
+ dependencies.push dep
334
+ else
335
+ raise "Unexpected dependency type #{dep.class} when parsing DGD Manifest specs, item: #{dep.inspect}"
336
+ end
337
+ end
338
+ end
339
+
340
+ spec = GoodsSpec.new(@repo, name: fields["name"], source: source, source_details: source_details, paths: fields["paths"], dependencies: dependencies)
260
341
  return spec
261
342
  end
262
343
  end
@@ -267,8 +348,9 @@ CONTENTS
267
348
  attr_reader :source
268
349
  attr_reader :source_details
269
350
  attr_reader :paths
351
+ attr_reader :dependencies
270
352
 
271
- def initialize(repo, name:, source:, source_details: {}, paths:)
353
+ def initialize(repo, name:, source:, source_details: {}, paths:, dependencies:)
272
354
  @repo = repo
273
355
  @name = name
274
356
  @source = source
@@ -281,6 +363,211 @@ CONTENTS
281
363
  end
282
364
 
283
365
  @paths = cleaned_paths
366
+ @dependencies = dependencies
367
+ end
368
+ end
369
+
370
+ class AppDirectory
371
+ attr_reader :location
372
+ attr_accessor :name
373
+
374
+ DEFAULT_FILE_LOCATIONS = {
375
+ "manifest" => "dgd.manifest",
376
+ "gitignore" => ".gitignore",
377
+ "gems_rb" => "gems.rb",
378
+ }
379
+ DEFAULT_EMPTY_DIRS = [ "app", "state" ]
380
+
381
+ def initialize(directory)
382
+ @location = directory
383
+ end
384
+
385
+ def gitignore_contents
386
+ <<~FILE_CONTENTS
387
+ # DGD Manifest files
388
+ .root
389
+ dgd.config
390
+ state/*
391
+ FILE_CONTENTS
392
+ end
393
+
394
+ def manifest_contents
395
+ <<FILE_CONTENTS
396
+ {
397
+ "name": "#{@name}",
398
+ "version": "0.1.0",
399
+ "description": "TODO: put description here",
400
+ "app_root": "app",
401
+ "goods": [
402
+ "# This is an example goods file - substitute your own.",
403
+ "https://raw.githubusercontent.com/noahgibbs/dgd-tools/main/goods/skotos_httpd.goods"
404
+ ],
405
+ "unbundled_goods": [
406
+ {
407
+ "#": "this is an example of unbundled goods - substitute your own",
408
+ "name": "kernellib",
409
+ "git": {
410
+ "url": "https://github.com/ChatTheatre/kernellib.git",
411
+ "branch": "master"
412
+ },
413
+ "paths": {
414
+ "src/doc/kernel": "doc/kernel",
415
+ "src/include/kernel": "include/kernel",
416
+ "src/include/*.h": "include",
417
+ "src/kernel": "kernel"
418
+ }
419
+ }
420
+ ]
421
+ }
422
+ FILE_CONTENTS
423
+ end
424
+
425
+ def gems_rb_contents
426
+ <<~FILE_CONTENTS
427
+ source "https://rubygems.org"
428
+
429
+ gem "dgd-tools", ">= #{DGD::VERSION}"
430
+ FILE_CONTENTS
431
+ end
432
+
433
+ def create!
434
+ if File.exist?(@location) && (!File.directory?(@location) || Dir["#{@location}/**"].size != 0)
435
+ raise "Cannot create a new DGD manifest project over a file or in an existing non-empty directory!"
436
+ end
437
+
438
+ puts "Creating new DGD manifest project at #{@location}..."
439
+ FileUtils.mkdir_p @location
440
+ Dir.chdir @location do
441
+ DEFAULT_FILE_LOCATIONS.each do |file_desc, file_location|
442
+ File.open(file_location, "wb") do |f|
443
+ contents = send("#{file_desc}_contents")
444
+ f.write(contents)
445
+ end
446
+ end
447
+
448
+ DEFAULT_EMPTY_DIRS.each do |dir|
449
+ FileUtils.mkdir dir
450
+ FileUtils.touch File.join(dir, ".keep")
451
+ end
452
+
453
+ result = system "bundle"
454
+ raise("Could not run bundler to install dgd-tools for #{@location}!") unless result
455
+
456
+ result = system "bundle exec dgd-manifest install"
457
+ raise("Error when running dgd-manifest for #{@location}!") unless result
458
+ end
459
+
460
+ puts "Successfully created project at #{@location}."
461
+ end
462
+ end
463
+
464
+ class DGDRuntimeConfig
465
+ attr_reader :app_root
466
+
467
+ DEFAULT_CONFIG = {
468
+ users: 100,
469
+ editors: 40,
470
+ swap_size: 1048576,
471
+ sector_size: 512,
472
+ swap_fragment: 4096,
473
+ static_chunk: 64512,
474
+ dynamic_chunk: 261120,
475
+ dump_interval: 3600,
476
+ typechecking: 2,
477
+ include_file: "/include/std.h",
478
+ include_dirs: ["/include", "~/include"],
479
+ auto_object: "/kernel/lib/auto",
480
+ driver_object: "/kernel/sys/driver",
481
+ create: "_F_create",
482
+ array_size: 16384,
483
+ objects: 262144,
484
+ call_outs: 16384,
485
+ }
486
+ CONFIG_KEYS = DEFAULT_CONFIG.keys.map(&:to_s) + [ "app_root", "ports", "telnet_ports", "dump_file", "statedir" ]
487
+
488
+ def initialize(config_data)
489
+ @app_root = config_data["app_root"] || "app"
490
+ @ports = {
491
+ "*" => 50100,
492
+ }
493
+ @telnet_ports = {
494
+ "*" => 50110,
495
+ }
496
+ @statedir = config_data["statedir"] || "state"
497
+ @dump_file = if config_data["dump_file"]
498
+ "../" + config_data["dump_file"]
499
+ else
500
+ "../#{@statedir}/dump"
501
+ end
502
+ @config = DEFAULT_CONFIG.dup
503
+
504
+ @raw_data = config_data
505
+ @config.keys.each do |prop|
506
+ # For now, assume and require that JSON data is the correct type if present
507
+ @config[prop] = config_data[prop.to_s] if config_data[prop.to_s]
508
+ end
509
+ unexpected_config_keys = config_data.keys - CONFIG_KEYS
510
+ unless unexpected_config_keys.empty?
511
+ raise "Unexpected key names in DGD configuration: #{unexpected_config_keys.inspect}!"
512
+ end
513
+
514
+ if config_data["telnet_ports"]
515
+ @telnet_ports = config_to_ports(config_data["telnet_ports"])
516
+ end
517
+ if config_data["ports"]
518
+ @ports = config_to_ports(config_data["ports"])
519
+ end
520
+ end
521
+
522
+ def config_to_ports(data)
523
+ if data.is_a?(Hash)
524
+ # TODO: verify that keys are IP addr strings and values are legal port numbers
525
+ return data
526
+ elsif data.is_a?(Array)
527
+ # TODO: verify that data is an array of legal integer port numbers
528
+ ports = {}
529
+ data.each { |p| ports["*"] = p }
530
+ return ports
531
+ elsif data.is_a?(Integer)
532
+ return { "*": data }
533
+ else
534
+ raise "dgd-manifest: not sure how to get port data from a #{data.class.name} -- #{data.inspect}!"
535
+ end
536
+ end
537
+
538
+ def as_file
539
+ return <<DGD_CONFIG
540
+ telnet_port = ([
541
+ #{@telnet_ports.map { |ip, p| "#{ip.inspect}:#{p}" }.join(",\n ") }
542
+ ]); /* legacy telnet ports */
543
+ binary_port = ([
544
+ #{@ports.map { |ip, p| "#{ip.inspect}:#{p}" }.join(",\n ") }
545
+ ]); /* binary ports */
546
+ directory = "./#{GENERATED_ROOT}";
547
+
548
+ users = #{@config[:users]}; /* max # of connections */
549
+ editors = #{@config[:editors]}; /* max # of built-in-editor sessions */
550
+ ed_tmpfile = "../#{@statedir}/ed"; /* proto editor tmpfile */
551
+ swap_file = "../#{@statedir}/swap"; /* swap file */
552
+ swap_size = #{@config[:swap_size]}; /* # sectors in swap file */
553
+ sector_size = #{@config[:sector_size]}; /* swap sector size */
554
+ swap_fragment = #{@config[:swap_fragment]}; /* fragment to swap out */
555
+ static_chunk = #{@config[:static_chunk]}; /* static memory chunk */
556
+ dynamic_chunk = #{@config[:dynamic_chunk]}; /* dynamic memory chunk */
557
+ dump_file = #{@dump_file.inspect}; /* dump file */
558
+ dump_interval = #{@config[:dump_interval]}; /* expected statedump interval in seconds */
559
+
560
+ typechecking = #{@config[:typechecking]}; /* level of typechecking (2 is highest) */
561
+ include_file = #{@config[:include_file].inspect}; /* standard include file */
562
+ include_dirs = ({ #{@config[:include_dirs].map(&:inspect).join(", ")} }); /* directories to search */
563
+ auto_object = #{@config[:auto_object].inspect}; /* auto inherited object */
564
+ driver_object = #{@config[:driver_object].inspect}; /* driver object */
565
+ create = #{@config[:create].inspect}; /* name of create function */
566
+
567
+ array_size = #{@config[:array_size]}; /* max array size */
568
+ objects = #{@config[:objects]}; /* max # of objects */
569
+ call_outs = #{@config[:call_outs]}; /* max # of callouts */
570
+ DGD_CONFIG
284
571
  end
285
572
  end
286
573
  end