xref: /freebsd/stand/forth/support.4th (revision e307eb94ae520d98dc1d346a0c53667a41beab5d)
1\ Copyright (c) 1999 Daniel C. Sobral <dcs@FreeBSD.org>
2\ All rights reserved.
3\
4\ Redistribution and use in source and binary forms, with or without
5\ modification, are permitted provided that the following conditions
6\ are met:
7\ 1. Redistributions of source code must retain the above copyright
8\    notice, this list of conditions and the following disclaimer.
9\ 2. Redistributions in binary form must reproduce the above copyright
10\    notice, this list of conditions and the following disclaimer in the
11\    documentation and/or other materials provided with the distribution.
12\
13\ THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
14\ ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
15\ IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
16\ ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
17\ FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
18\ DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
19\ OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
20\ HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
21\ LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
22\ OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
23\ SUCH DAMAGE.
24\
25\ $FreeBSD$
26
27\ Loader.rc support functions:
28\
29\ initialize ( addr len -- )	as above, plus load_conf_files
30\ load_conf ( addr len -- )	load conf file given
31\ include_conf_files ( -- )	load all conf files in load_conf_files
32\ print_syntax_error ( -- )	print line and marker of where a syntax
33\				error was detected
34\ print_line ( -- )		print last line processed
35\ load_kernel ( -- )		load kernel
36\ load_modules ( -- )		load modules flagged
37\
38\ Exported structures:
39\
40\ string			counted string structure
41\	cell .addr			string address
42\	cell .len			string length
43\ module			module loading information structure
44\	cell module.flag		should we load it?
45\	string module.name		module's name
46\	string module.loadname		name to be used in loading the module
47\	string module.type		module's type
48\	string module.args		flags to be passed during load
49\	string module.beforeload	command to be executed before load
50\	string module.afterload		command to be executed after load
51\	string module.loaderror		command to be executed if load fails
52\	cell module.next		list chain
53\
54\ Exported global variables;
55\
56\ string conf_files		configuration files to be loaded
57\ cell modules_options		pointer to first module information
58\ value verbose?		indicates if user wants a verbose loading
59\ value any_conf_read?		indicates if a conf file was successfully read
60\
61\ Other exported words:
62\    note, strlen is internal
63\ strdup ( addr len -- addr' len)			similar to strdup(3)
64\ strcat ( addr len addr' len' -- addr len+len' )	similar to strcat(3)
65\ s' ( | string' -- addr len | )			similar to s"
66\ rudimentary structure support
67
68\ Exception values
69
701 constant ESYNTAX
712 constant ENOMEM
723 constant EFREE
734 constant ESETERROR	\ error setting environment variable
745 constant EREAD	\ error reading
756 constant EOPEN
767 constant EEXEC	\ XXX never catched
778 constant EBEFORELOAD
789 constant EAFTERLOAD
79
80\ I/O constants
81
820 constant SEEK_SET
831 constant SEEK_CUR
842 constant SEEK_END
85
860 constant O_RDONLY
871 constant O_WRONLY
882 constant O_RDWR
89
90\ Crude structure support
91
92: structure:
93  create here 0 , ['] drop , 0
94  does> create here swap dup @ allot cell+ @ execute
95;
96: member: create dup , over , + does> cell+ @ + ;
97: ;structure swap ! ;
98: constructor! >body cell+ ! ;
99: constructor: over :noname ;
100: ;constructor postpone ; swap cell+ ! ; immediate
101: sizeof ' >body @ state @ if postpone literal then ; immediate
102: offsetof ' >body cell+ @ state @ if postpone literal then ; immediate
103: ptr 1 cells member: ;
104: int 1 cells member: ;
105
106\ String structure
107
108structure: string
109	ptr .addr
110	int .len
111	constructor:
112	  0 over .addr !
113	  0 swap .len !
114	;constructor
115;structure
116
117
118\ Module options linked list
119
120structure: module
121	int module.flag
122	sizeof string member: module.name
123	sizeof string member: module.loadname
124	sizeof string member: module.type
125	sizeof string member: module.args
126	sizeof string member: module.beforeload
127	sizeof string member: module.afterload
128	sizeof string member: module.loaderror
129	ptr module.next
130;structure
131
132\ Internal loader structures (preloaded_file, kernel_module, file_metadata)
133\ must be in sync with the C struct in stand/common/bootstrap.h
134structure: preloaded_file
135	ptr pf.name
136	ptr pf.type
137	ptr pf.args
138	ptr pf.metadata	\ file_metadata
139	int pf.loader
140	int pf.addr
141	int pf.size
142	ptr pf.modules	\ kernel_module
143	ptr pf.next	\ preloaded_file
144;structure
145
146structure: kernel_module
147	ptr km.name
148	\ ptr km.args
149	ptr km.fp	\ preloaded_file
150	ptr km.next	\ kernel_module
151;structure
152
153structure: file_metadata
154	int		md.size
155	2 member:	md.type	\ this is not ANS Forth compatible (XXX)
156	ptr		md.next	\ file_metadata
157	0 member:	md.data	\ variable size
158;structure
159
160\ end of structures
161
162\ Global variables
163
164string conf_files
165string nextboot_conf_file
166create module_options sizeof module.next allot 0 module_options !
167create last_module_option sizeof module.next allot 0 last_module_option !
1680 value verbose?
1690 value nextboot?
170
171\ Support string functions
172: strdup { addr len -- addr' len' }
173  len allocate if ENOMEM throw then
174  addr over len move len
175;
176
177: strcat  { addr len addr' len' -- addr len+len' }
178  addr' addr len + len' move
179  addr len len' +
180;
181
182: strchr { addr len c -- addr' len' }
183  begin
184    len
185  while
186    addr c@ c = if addr len exit then
187    addr 1 + to addr
188    len 1 - to len
189  repeat
190  0 0
191;
192
193: s' \ same as s", allows " in the string
194  [char] ' parse
195  state @ if postpone sliteral then
196; immediate
197
198: 2>r postpone >r postpone >r ; immediate
199: 2r> postpone r> postpone r> ; immediate
200: 2r@ postpone 2r> postpone 2dup postpone 2>r ; immediate
201
202: getenv?  getenv -1 = if false else drop true then ;
203
204\ determine if a word appears in a string, case-insensitive
205: contains? ( addr1 len1 addr2 len2 -- 0 | -1 )
206	2 pick 0= if 2drop 2drop true exit then
207	dup 0= if 2drop 2drop false exit then
208	begin
209		begin
210			swap dup c@ dup 32 = over 9 = or over 10 = or
211			over 13 = or over 44 = or swap drop
212		while 1+ swap 1- repeat
213		swap 2 pick 1- over <
214	while
215		2over 2over drop over compare-insensitive 0= if
216			2 pick over = if 2drop 2drop true exit then
217			2 pick tuck - -rot + swap over c@ dup 32 =
218			over 9 = or over 10 = or over 13 = or over 44 = or
219			swap drop if 2drop 2drop true exit then
220		then begin
221			swap dup c@ dup 32 = over 9 = or over 10 = or
222			over 13 = or over 44 = or swap drop
223			if false else true then 2 pick 0> and
224		while 1+ swap 1- repeat
225		swap
226	repeat
227	2drop 2drop false
228;
229
230: boot_serial? ( -- 0 | -1 )
231	s" console" getenv dup -1 <> if
232		s" comconsole" 2swap contains?
233	else drop false then
234	s" boot_serial" getenv dup -1 <> if
235		swap drop 0>
236	else drop false then
237	or \ console contains comconsole ( or ) boot_serial
238	s" boot_multicons" getenv dup -1 <> if
239		swap drop 0>
240	else drop false then
241	or \ previous boolean ( or ) boot_multicons
242;
243
244\ Private definitions
245
246vocabulary support-functions
247only forth also support-functions definitions
248
249\ Some control characters constants
250
2517 constant bell
2528 constant backspace
2539 constant tab
25410 constant lf
25513 constant <cr>
256
257\ Read buffer size
258
25980 constant read_buffer_size
260
261\ Standard suffixes
262
263: load_module_suffix		s" _load" ;
264: module_loadname_suffix	s" _name" ;
265: module_type_suffix		s" _type" ;
266: module_args_suffix		s" _flags" ;
267: module_beforeload_suffix	s" _before" ;
268: module_afterload_suffix	s" _after" ;
269: module_loaderror_suffix	s" _error" ;
270
271\ Support operators
272
273: >= < 0= ;
274: <= > 0= ;
275
276\ Assorted support functions
277
278: free-memory free if EFREE throw then ;
279
280: strget { var -- addr len } var .addr @ var .len @ ;
281
282\ assign addr len to variable.
283: strset  { addr len var -- } addr var .addr !  len var .len !  ;
284
285\ free memory and reset fields
286: strfree { var -- } var .addr @ ?dup if free-memory 0 0 var strset then ;
287
288\ free old content, make a copy of the string and assign to variable
289: string= { addr len var -- } var strfree addr len strdup var strset ;
290
291: strtype ( str -- ) strget type ;
292
293\ assign a reference to what is on the stack
294: strref { addr len var -- addr len }
295  addr var .addr ! len var .len ! addr len
296;
297
298\ unquote a string
299: unquote ( addr len -- addr len )
300  over c@ [char] " = if 2 chars - swap char+ swap then
301;
302
303\ Assignment data temporary storage
304
305string name_buffer
306string value_buffer
307
308\ Line by line file reading functions
309\
310\ exported:
311\	line_buffer
312\	end_of_file?
313\	fd
314\	read_line
315\	reset_line_reading
316
317vocabulary line-reading
318also line-reading definitions
319
320\ File data temporary storage
321
322string read_buffer
3230 value read_buffer_ptr
324
325\ File's line reading function
326
327get-current ( -- wid ) previous definitions
328
329string line_buffer
3300 value end_of_file?
331variable fd
332
333>search ( wid -- ) definitions
334
335: skip_newlines
336  begin
337    read_buffer .len @ read_buffer_ptr >
338  while
339    read_buffer .addr @ read_buffer_ptr + c@ lf = if
340      read_buffer_ptr char+ to read_buffer_ptr
341    else
342      exit
343    then
344  repeat
345;
346
347: scan_buffer  ( -- addr len )
348  read_buffer_ptr >r
349  begin
350    read_buffer .len @ r@ >
351  while
352    read_buffer .addr @ r@ + c@ lf = if
353      read_buffer .addr @ read_buffer_ptr +  ( -- addr )
354      r@ read_buffer_ptr -                   ( -- len )
355      r> to read_buffer_ptr
356      exit
357    then
358    r> char+ >r
359  repeat
360  read_buffer .addr @ read_buffer_ptr +  ( -- addr )
361  r@ read_buffer_ptr -                   ( -- len )
362  r> to read_buffer_ptr
363;
364
365: line_buffer_resize  ( len -- len )
366  dup 0= if exit then
367  >r
368  line_buffer .len @ if
369    line_buffer .addr @
370    line_buffer .len @ r@ +
371    resize if ENOMEM throw then
372  else
373    r@ allocate if ENOMEM throw then
374  then
375  line_buffer .addr !
376  r>
377;
378
379: append_to_line_buffer  ( addr len -- )
380  dup 0= if 2drop exit then
381  line_buffer strget
382  2swap strcat
383  line_buffer .len !
384  drop
385;
386
387: read_from_buffer
388  scan_buffer            ( -- addr len )
389  line_buffer_resize     ( len -- len )
390  append_to_line_buffer  ( addr len -- )
391;
392
393: refill_required?
394  read_buffer .len @ read_buffer_ptr =
395  end_of_file? 0= and
396;
397
398: refill_buffer
399  0 to read_buffer_ptr
400  read_buffer .addr @ 0= if
401    read_buffer_size allocate if ENOMEM throw then
402    read_buffer .addr !
403  then
404  fd @ read_buffer .addr @ read_buffer_size fread
405  dup -1 = if EREAD throw then
406  dup 0= if true to end_of_file? then
407  read_buffer .len !
408;
409
410get-current ( -- wid ) previous definitions >search ( wid -- )
411
412: reset_line_reading
413  0 to read_buffer_ptr
414;
415
416: read_line
417  line_buffer strfree
418  skip_newlines
419  begin
420    read_from_buffer
421    refill_required?
422  while
423    refill_buffer
424  repeat
425;
426
427only forth also support-functions definitions
428
429\ Conf file line parser:
430\ <line> ::= <spaces><name><spaces>'='<spaces><value><spaces>[<comment>] |
431\            <spaces>[<comment>]
432\ <name> ::= <letter>{<letter>|<digit>|'_'}
433\ <value> ::= '"'{<character_set>|'\'<anything>}'"' | <name>
434\ <character_set> ::= ASCII 32 to 126, except '\' and '"'
435\ <comment> ::= '#'{<anything>}
436\
437\ exported:
438\	line_pointer
439\	process_conf
440
4410 value line_pointer
442
443vocabulary file-processing
444also file-processing definitions
445
446\ parser functions
447\
448\ exported:
449\	get_assignment
450
451vocabulary parser
452also parser definitions
453
4540 value parsing_function
4550 value end_of_line
456
457: end_of_line?  line_pointer end_of_line = ;
458
459\ classifiers for various character classes in the input line
460
461: letter?
462  line_pointer c@ >r
463  r@ [char] A >=
464  r@ [char] Z <= and
465  r@ [char] a >=
466  r> [char] z <= and
467  or
468;
469
470: digit?
471  line_pointer c@ >r
472  r@ [char] - =
473  r@ [char] 0 >=
474  r> [char] 9 <= and
475  or
476;
477
478: quote?  line_pointer c@ [char] " = ;
479
480: assignment_sign?  line_pointer c@ [char] = = ;
481
482: comment?  line_pointer c@ [char] # = ;
483
484: space?  line_pointer c@ bl = line_pointer c@ tab = or ;
485
486: backslash?  line_pointer c@ [char] \ = ;
487
488: underscore?  line_pointer c@ [char] _ = ;
489
490: dot?  line_pointer c@ [char] . = ;
491
492\ manipulation of input line
493: skip_character line_pointer char+ to line_pointer ;
494
495: skip_to_end_of_line end_of_line to line_pointer ;
496
497: eat_space
498  begin
499    end_of_line? if 0 else space? then
500  while
501    skip_character
502  repeat
503;
504
505: parse_name  ( -- addr len )
506  line_pointer
507  begin
508    end_of_line? if 0 else letter? digit? underscore? dot? or or or then
509  while
510    skip_character
511  repeat
512  line_pointer over -
513  strdup
514;
515
516: remove_backslashes  { addr len | addr' len' -- addr' len' }
517  len allocate if ENOMEM throw then
518  to addr'
519  addr >r
520  begin
521    addr c@ [char] \ <> if
522      addr c@ addr' len' + c!
523      len' char+ to len'
524    then
525    addr char+ to addr
526    r@ len + addr =
527  until
528  r> drop
529  addr' len'
530;
531
532: parse_quote  ( -- addr len )
533  line_pointer
534  skip_character
535  end_of_line? if ESYNTAX throw then
536  begin
537    quote? 0=
538  while
539    backslash? if
540      skip_character
541      end_of_line? if ESYNTAX throw then
542    then
543    skip_character
544    end_of_line? if ESYNTAX throw then
545  repeat
546  skip_character
547  line_pointer over -
548  remove_backslashes
549;
550
551: read_name
552  parse_name		( -- addr len )
553  name_buffer strset
554;
555
556: read_value
557  quote? if
558    parse_quote		( -- addr len )
559  else
560    parse_name		( -- addr len )
561  then
562  value_buffer strset
563;
564
565: comment
566  skip_to_end_of_line
567;
568
569: white_space_4
570  eat_space
571  comment? if ['] comment to parsing_function exit then
572  end_of_line? 0= if ESYNTAX throw then
573;
574
575: variable_value
576  read_value
577  ['] white_space_4 to parsing_function
578;
579
580: white_space_3
581  eat_space
582  letter? digit? quote? or or if
583    ['] variable_value to parsing_function exit
584  then
585  ESYNTAX throw
586;
587
588: assignment_sign
589  skip_character
590  ['] white_space_3 to parsing_function
591;
592
593: white_space_2
594  eat_space
595  assignment_sign? if ['] assignment_sign to parsing_function exit then
596  ESYNTAX throw
597;
598
599: variable_name
600  read_name
601  ['] white_space_2 to parsing_function
602;
603
604: white_space_1
605  eat_space
606  letter?  if ['] variable_name to parsing_function exit then
607  comment? if ['] comment to parsing_function exit then
608  end_of_line? 0= if ESYNTAX throw then
609;
610
611get-current ( -- wid ) previous definitions >search ( wid -- )
612
613: get_assignment
614  line_buffer strget + to end_of_line
615  line_buffer .addr @ to line_pointer
616  ['] white_space_1 to parsing_function
617  begin
618    end_of_line? 0=
619  while
620    parsing_function execute
621  repeat
622  parsing_function ['] comment =
623  parsing_function ['] white_space_1 =
624  parsing_function ['] white_space_4 =
625  or or 0= if ESYNTAX throw then
626;
627
628only forth also support-functions also file-processing definitions
629
630\ Process line
631
632: assignment_type?  ( addr len -- flag )
633  name_buffer strget
634  compare 0=
635;
636
637: suffix_type?  ( addr len -- flag )
638  name_buffer .len @ over <= if 2drop false exit then
639  name_buffer .len @ over - name_buffer .addr @ +
640  over compare 0=
641;
642
643: loader_conf_files?  s" loader_conf_files" assignment_type?  ;
644
645: nextboot_flag?  s" nextboot_enable" assignment_type?  ;
646
647: nextboot_conf? s" nextboot_conf" assignment_type?  ;
648
649: verbose_flag? s" verbose_loading" assignment_type?  ;
650
651: execute? s" exec" assignment_type?  ;
652
653: module_load? load_module_suffix suffix_type? ;
654
655: module_loadname?  module_loadname_suffix suffix_type?  ;
656
657: module_type?  module_type_suffix suffix_type?  ;
658
659: module_args?  module_args_suffix suffix_type?  ;
660
661: module_beforeload?  module_beforeload_suffix suffix_type?  ;
662
663: module_afterload?  module_afterload_suffix suffix_type?  ;
664
665: module_loaderror?  module_loaderror_suffix suffix_type?  ;
666
667\ build a 'set' statement and execute it
668: set_environment_variable
669  name_buffer .len @ value_buffer .len @ + 5 chars + \ size of result string
670  allocate if ENOMEM throw then
671  dup 0  \ start with an empty string and append the pieces
672  s" set " strcat
673  name_buffer strget strcat
674  s" =" strcat
675  value_buffer strget strcat
676  ['] evaluate catch if
677    2drop free drop
678    ESETERROR throw
679  else
680    free-memory
681  then
682;
683
684: set_conf_files
685  set_environment_variable
686  s" loader_conf_files" getenv conf_files string=
687;
688
689: set_nextboot_conf
690  value_buffer strget unquote nextboot_conf_file string=
691;
692
693: append_to_module_options_list  ( addr -- )
694  module_options @ 0= if
695    dup module_options !
696    last_module_option !
697  else
698    dup last_module_option @ module.next !
699    last_module_option !
700  then
701;
702
703: set_module_name  { addr -- }	\ check leaks
704  name_buffer strget addr module.name string=
705;
706
707: yes_value?
708  value_buffer strget	\ XXX could use unquote
709  2dup s' "YES"' compare >r
710  2dup s' "yes"' compare >r
711  2dup s" YES" compare >r
712  s" yes" compare r> r> r> and and and 0=
713;
714
715: find_module_option  ( -- addr | 0 ) \ return ptr to entry matching name_buffer
716  module_options @
717  begin
718    dup
719  while
720    dup module.name strget
721    name_buffer strget
722    compare 0= if exit then
723    module.next @
724  repeat
725;
726
727: new_module_option  ( -- addr )
728  sizeof module allocate if ENOMEM throw then
729  dup sizeof module erase
730  dup append_to_module_options_list
731  dup set_module_name
732;
733
734: get_module_option  ( -- addr )
735  find_module_option
736  ?dup 0= if new_module_option then
737;
738
739: set_module_flag
740  name_buffer .len @ load_module_suffix nip - name_buffer .len !
741  yes_value? get_module_option module.flag !
742;
743
744: set_module_args
745  name_buffer .len @ module_args_suffix nip - name_buffer .len !
746  value_buffer strget unquote
747  get_module_option module.args string=
748;
749
750: set_module_loadname
751  name_buffer .len @ module_loadname_suffix nip - name_buffer .len !
752  value_buffer strget unquote
753  get_module_option module.loadname string=
754;
755
756: set_module_type
757  name_buffer .len @ module_type_suffix nip - name_buffer .len !
758  value_buffer strget unquote
759  get_module_option module.type string=
760;
761
762: set_module_beforeload
763  name_buffer .len @ module_beforeload_suffix nip - name_buffer .len !
764  value_buffer strget unquote
765  get_module_option module.beforeload string=
766;
767
768: set_module_afterload
769  name_buffer .len @ module_afterload_suffix nip - name_buffer .len !
770  value_buffer strget unquote
771  get_module_option module.afterload string=
772;
773
774: set_module_loaderror
775  name_buffer .len @ module_loaderror_suffix nip - name_buffer .len !
776  value_buffer strget unquote
777  get_module_option module.loaderror string=
778;
779
780: set_nextboot_flag
781  yes_value? to nextboot?
782;
783
784: set_verbose
785  yes_value? to verbose?
786;
787
788: execute_command
789  value_buffer strget unquote
790  ['] evaluate catch if EEXEC throw then
791;
792
793: process_assignment
794  name_buffer .len @ 0= if exit then
795  loader_conf_files?	if set_conf_files exit then
796  nextboot_flag?	if set_nextboot_flag exit then
797  nextboot_conf?	if set_nextboot_conf exit then
798  verbose_flag?		if set_verbose exit then
799  execute?		if execute_command exit then
800  module_load?		if set_module_flag exit then
801  module_loadname?	if set_module_loadname exit then
802  module_type?		if set_module_type exit then
803  module_args?		if set_module_args exit then
804  module_beforeload?	if set_module_beforeload exit then
805  module_afterload?	if set_module_afterload exit then
806  module_loaderror?	if set_module_loaderror exit then
807  set_environment_variable
808;
809
810\ free_buffer  ( -- )
811\
812\ Free some pointers if needed. The code then tests for errors
813\ in freeing, and throws an exception if needed. If a pointer is
814\ not allocated, it's value (0) is used as flag.
815
816: free_buffers
817  name_buffer strfree
818  value_buffer strfree
819;
820
821\ Higher level file processing
822
823get-current ( -- wid ) previous definitions >search ( wid -- )
824
825: process_conf
826  begin
827    end_of_file? 0=
828  while
829    free_buffers
830    read_line
831    get_assignment
832    ['] process_assignment catch
833    ['] free_buffers catch
834    swap throw throw
835  repeat
836;
837
838: peek_file ( addr len -- )
839  0 to end_of_file?
840  reset_line_reading
841  O_RDONLY fopen fd !
842  fd @ -1 = if EOPEN throw then
843  free_buffers
844  read_line
845  get_assignment
846  ['] process_assignment catch
847  ['] free_buffers catch
848  fd @ fclose
849  swap throw throw
850;
851
852only forth also support-functions definitions
853
854\ Interface to loading conf files
855
856: load_conf  ( addr len -- )
857  0 to end_of_file?
858  reset_line_reading
859  O_RDONLY fopen fd !
860  fd @ -1 = if EOPEN throw then
861  ['] process_conf catch
862  fd @ fclose
863  throw
864;
865
866: print_line line_buffer strtype cr ;
867
868: print_syntax_error
869  line_buffer strtype cr
870  line_buffer .addr @
871  begin
872    line_pointer over <>
873  while
874    bl emit char+
875  repeat
876  drop
877  ." ^" cr
878;
879
880
881\ Debugging support functions
882
883only forth definitions also support-functions
884
885: test-file
886  ['] load_conf catch dup .
887  ESYNTAX = if cr print_syntax_error then
888;
889
890\ find a module name, leave addr on the stack (0 if not found)
891: find-module ( <module> -- ptr | 0 )
892  bl parse ( addr len )
893  module_options @ >r ( store current pointer )
894  begin
895    r@
896  while
897    2dup ( addr len addr len )
898    r@ module.name strget
899    compare 0= if drop drop r> exit then ( found it )
900    r> module.next @ >r
901  repeat
902  type ."  was not found" cr r>
903;
904
905: show-nonempty ( addr len mod -- )
906  strget dup verbose? or if
907    2swap type type cr
908  else
909    drop drop drop drop
910  then ;
911
912: show-one-module { addr -- addr }
913  ." Name:        " addr module.name strtype cr
914  s" Path:        " addr module.loadname show-nonempty
915  s" Type:        " addr module.type show-nonempty
916  s" Flags:       " addr module.args show-nonempty
917  s" Before load: " addr module.beforeload show-nonempty
918  s" After load:  " addr module.afterload show-nonempty
919  s" Error:       " addr module.loaderror show-nonempty
920  ." Status:      " addr module.flag @ if ." Load" else ." Don't load" then cr
921  cr
922  addr
923;
924
925: show-module-options
926  module_options @
927  begin
928    ?dup
929  while
930    show-one-module
931    module.next @
932  repeat
933;
934
935: free-one-module { addr -- addr }
936  addr module.name strfree
937  addr module.loadname strfree
938  addr module.type strfree
939  addr module.args strfree
940  addr module.beforeload strfree
941  addr module.afterload strfree
942  addr module.loaderror strfree
943  addr
944;
945
946: free-module-options
947  module_options @
948  begin
949    ?dup
950  while
951    free-one-module
952    dup module.next @
953    swap free-memory
954  repeat
955  0 module_options !
956  0 last_module_option !
957;
958
959only forth also support-functions definitions
960
961\ Variables used for processing multiple conf files
962
963string current_file_name_ref	\ used to print the file name
964
965\ Indicates if any conf file was successfully read
966
9670 value any_conf_read?
968
969\ loader_conf_files processing support functions
970
971: get_conf_files ( -- addr len )  \ put addr/len on stack, reset var
972  conf_files strget 0 0 conf_files strset
973;
974
975: skip_leading_spaces  { addr len pos -- addr len pos' }
976  begin
977    pos len = if 0 else addr pos + c@ bl = then
978  while
979    pos char+ to pos
980  repeat
981  addr len pos
982;
983
984\ return the file name at pos, or free the string if nothing left
985: get_file_name  { addr len pos -- addr len pos' addr' len' || 0 }
986  pos len = if
987    addr free abort" Fatal error freeing memory"
988    0 exit
989  then
990  pos >r
991  begin
992    \ stay in the loop until have chars and they are not blank
993    pos len = if 0 else addr pos + c@ bl <> then
994  while
995    pos char+ to pos
996  repeat
997  addr len pos addr r@ + pos r> -
998;
999
1000: get_next_file  ( addr len ptr -- addr len ptr' addr' len' | 0 )
1001  skip_leading_spaces
1002  get_file_name
1003;
1004
1005: print_current_file
1006  current_file_name_ref strtype
1007;
1008
1009: process_conf_errors
1010  dup 0= if true to any_conf_read? drop exit then
1011  >r 2drop r>
1012  dup ESYNTAX = if
1013    ." Warning: syntax error on file " print_current_file cr
1014    print_syntax_error drop exit
1015  then
1016  dup ESETERROR = if
1017    ." Warning: bad definition on file " print_current_file cr
1018    print_line drop exit
1019  then
1020  dup EREAD = if
1021    ." Warning: error reading file " print_current_file cr drop exit
1022  then
1023  dup EOPEN = if
1024    verbose? if ." Warning: unable to open file " print_current_file cr then
1025    drop exit
1026  then
1027  dup EFREE = abort" Fatal error freeing memory"
1028  dup ENOMEM = abort" Out of memory"
1029  throw  \ Unknown error -- pass ahead
1030;
1031
1032\ Process loader_conf_files recursively
1033\ Interface to loader_conf_files processing
1034
1035: include_conf_files
1036  get_conf_files 0	( addr len offset )
1037  begin
1038    get_next_file ?dup ( addr len 1 | 0 )
1039  while
1040    current_file_name_ref strref
1041    ['] load_conf catch
1042    process_conf_errors
1043    conf_files .addr @ if recurse then
1044  repeat
1045;
1046
1047: get_nextboot_conf_file ( -- addr len )
1048  nextboot_conf_file strget
1049;
1050
1051: rewrite_nextboot_file ( -- )
1052  get_nextboot_conf_file
1053  O_WRONLY fopen fd !
1054  fd @ -1 = if EOPEN throw then
1055  fd @ s' nextboot_enable="NO" ' fwrite ( fd buf len -- nwritten ) drop
1056  fd @ fclose
1057;
1058
1059: include_nextboot_file ( -- )
1060  s" nextboot_enable" getenv dup -1 <> if
1061    2dup s' "YES"' compare >r
1062    2dup s' "yes"' compare >r
1063    2dup s" YES" compare >r
1064    2dup s" yes" compare r> r> r> and and and 0= to nextboot?
1065  else
1066    drop
1067    get_nextboot_conf_file
1068    ['] peek_file catch if 2drop then
1069  then
1070  nextboot? if
1071    get_nextboot_conf_file
1072    current_file_name_ref strref
1073    ['] load_conf catch
1074    process_conf_errors
1075    ['] rewrite_nextboot_file catch if 2drop then
1076  then
1077  s' "NO"' s" nextboot_enable" setenv
1078;
1079
1080\ Module loading functions
1081
1082: load_parameters  { addr -- addr addrN lenN ... addr1 len1 N }
1083  addr
1084  addr module.args strget
1085  addr module.loadname .len @ if
1086    addr module.loadname strget
1087  else
1088    addr module.name strget
1089  then
1090  addr module.type .len @ if
1091    addr module.type strget
1092    s" -t "
1093    4 ( -t type name flags )
1094  else
1095    2 ( name flags )
1096  then
1097;
1098
1099: before_load  ( addr -- addr )
1100  dup module.beforeload .len @ if
1101    dup module.beforeload strget
1102    ['] evaluate catch if EBEFORELOAD throw then
1103  then
1104;
1105
1106: after_load  ( addr -- addr )
1107  dup module.afterload .len @ if
1108    dup module.afterload strget
1109    ['] evaluate catch if EAFTERLOAD throw then
1110  then
1111;
1112
1113: load_error  ( addr -- addr )
1114  dup module.loaderror .len @ if
1115    dup module.loaderror strget
1116    evaluate  \ This we do not intercept so it can throw errors
1117  then
1118;
1119
1120: pre_load_message  ( addr -- addr )
1121  verbose? if
1122    dup module.name strtype
1123    ." ..."
1124  then
1125;
1126
1127: load_error_message verbose? if ." failed!" cr then ;
1128
1129: load_successful_message verbose? if ." ok" cr then ;
1130
1131: load_module
1132  load_parameters load
1133;
1134
1135: process_module  ( addr -- addr )
1136  pre_load_message
1137  before_load
1138  begin
1139    ['] load_module catch if
1140      dup module.loaderror .len @ if
1141        load_error			\ Command should return a flag!
1142      else
1143        load_error_message true		\ Do not retry
1144      then
1145    else
1146      after_load
1147      load_successful_message true	\ Successful, do not retry
1148    then
1149  until
1150;
1151
1152: process_module_errors  ( addr ior -- )
1153  dup EBEFORELOAD = if
1154    drop
1155    ." Module "
1156    dup module.name strtype
1157    dup module.loadname .len @ if
1158      ." (" dup module.loadname strtype ." )"
1159    then
1160    cr
1161    ." Error executing "
1162    dup module.beforeload strtype cr	\ XXX there was a typo here
1163    abort
1164  then
1165
1166  dup EAFTERLOAD = if
1167    drop
1168    ." Module "
1169    dup module.name .addr @ over module.name .len @ type
1170    dup module.loadname .len @ if
1171      ." (" dup module.loadname strtype ." )"
1172    then
1173    cr
1174    ." Error executing "
1175    dup module.afterload strtype cr
1176    abort
1177  then
1178
1179  throw  \ Don't know what it is all about -- pass ahead
1180;
1181
1182\ Module loading interface
1183
1184\ scan the list of modules, load enabled ones.
1185: load_modules  ( -- ) ( throws: abort & user-defined )
1186  module_options @	( list_head )
1187  begin
1188    ?dup
1189  while
1190    dup module.flag @ if
1191      ['] process_module catch
1192      process_module_errors
1193    then
1194    module.next @
1195  repeat
1196;
1197
1198\ h00h00 magic used to try loading either a kernel with a given name,
1199\ or a kernel with the default name in a directory of a given name
1200\ (the pain!)
1201
1202: bootpath s" /boot/" ;
1203: modulepath s" module_path" ;
1204
1205\ Functions used to save and restore module_path's value.
1206: saveenv ( addr len | -1 -- addr' len | 0 -1 )
1207  dup -1 = if 0 swap exit then
1208  strdup
1209;
1210: freeenv ( addr len | 0 -1 )
1211  -1 = if drop else free abort" Freeing error" then
1212;
1213: restoreenv  ( addr len | 0 -1 -- )
1214  dup -1 = if ( it wasn't set )
1215    2drop
1216    modulepath unsetenv
1217  else
1218    over >r
1219    modulepath setenv
1220    r> free abort" Freeing error"
1221  then
1222;
1223
1224: clip_args   \ Drop second string if only one argument is passed
1225  1 = if
1226    2swap 2drop
1227    1
1228  else
1229    2
1230  then
1231;
1232
1233also builtins
1234
1235\ Parse filename from a semicolon-separated list
1236
1237\ replacement, not working yet
1238: newparse-; { addr len | a1 -- a' len-x addr x }
1239  addr len [char] ; strchr dup if	( a1 len1 )
1240    swap to a1	( store address )
1241    1 - a1 @ 1 + swap ( remove match )
1242    addr a1 addr -
1243  else
1244    0 0 addr len
1245  then
1246;
1247
1248: parse-; ( addr len -- addr' len-x addr x )
1249  over 0 2swap			( addr 0 addr len )
1250  begin
1251    dup 0 <>			( addr 0 addr len )
1252  while
1253    over c@ [char] ; <>		( addr 0 addr len flag )
1254  while
1255    1- swap 1+ swap
1256    2swap 1+ 2swap
1257  repeat then
1258  dup 0 <> if
1259    1- swap 1+ swap
1260  then
1261  2swap
1262;
1263
1264\ Try loading one of multiple kernels specified
1265
1266: try_multiple_kernels ( addr len addr' len' args -- flag )
1267  >r
1268  begin
1269    parse-; 2>r
1270    2over 2r>
1271    r@ clip_args
1272    s" DEBUG" getenv? if
1273      s" echo Module_path: ${module_path}" evaluate
1274      ." Kernel     : " >r 2dup type r> cr
1275      dup 2 = if ." Flags      : " >r 2over type r> cr then
1276    then
1277    1 load
1278  while
1279    dup 0=
1280  until
1281    1 >r \ Failure
1282  else
1283    0 >r \ Success
1284  then
1285  2drop 2drop
1286  r>
1287  r> drop
1288;
1289
1290\ Try to load a kernel; the kernel name is taken from one of
1291\ the following lists, as ordered:
1292\
1293\   1. The "bootfile" environment variable
1294\   2. The "kernel" environment variable
1295\
1296\ Flags are passed, if available. If not, dummy values must be given.
1297\
1298\ The kernel gets loaded from the current module_path.
1299
1300: load_a_kernel ( flags len 1 | x x 0 -- flag )
1301  local args
1302  2local flags
1303  0 0 2local kernel
1304  end-locals
1305
1306  \ Check if a default kernel name exists at all, exits if not
1307  s" bootfile" getenv dup -1 <> if
1308    to kernel
1309    flags kernel args 1+ try_multiple_kernels
1310    dup 0= if exit then
1311  then
1312  drop
1313
1314  s" kernel" getenv dup -1 <> if
1315    to kernel
1316  else
1317    drop
1318    1 exit \ Failure
1319  then
1320
1321  \ Try all default kernel names
1322  flags kernel args 1+ try_multiple_kernels
1323;
1324
1325\ Try to load a kernel; the kernel name is taken from one of
1326\ the following lists, as ordered:
1327\
1328\   1. The "bootfile" environment variable
1329\   2. The "kernel" environment variable
1330\
1331\ Flags are passed, if provided.
1332\
1333\ The kernel will be loaded from a directory computed from the
1334\ path given. Two directories will be tried in the following order:
1335\
1336\   1. /boot/path
1337\   2. path
1338\
1339\ The module_path variable is overridden if load is successful, by
1340\ prepending the successful path.
1341
1342: load_from_directory ( path len 1 | flags len' path len 2 -- flag )
1343  local args
1344  2local path
1345  args 1 = if 0 0 then
1346  2local flags
1347  0 0 2local oldmodulepath \ like a string
1348  0 0 2local newmodulepath \ like a string
1349  end-locals
1350
1351  \ Set the environment variable module_path, and try loading
1352  \ the kernel again.
1353  modulepath getenv saveenv to oldmodulepath
1354
1355  \ Try prepending /boot/ first
1356  bootpath nip path nip + 	\ total length
1357  oldmodulepath nip dup -1 = if
1358    drop
1359  else
1360    1+ +			\ add oldpath -- XXX why the 1+ ?
1361  then
1362  allocate if ( out of memory ) 1 exit then \ XXX throw ?
1363
1364  0
1365  bootpath strcat
1366  path strcat
1367  2dup to newmodulepath
1368  modulepath setenv
1369
1370  \ Try all default kernel names
1371  flags args 1- load_a_kernel
1372  0= if ( success )
1373    oldmodulepath nip -1 <> if
1374      newmodulepath s" ;" strcat
1375      oldmodulepath strcat
1376      modulepath setenv
1377      newmodulepath drop free-memory
1378      oldmodulepath drop free-memory
1379    then
1380    0 exit
1381  then
1382
1383  \ Well, try without the prepended /boot/
1384  path newmodulepath drop swap move
1385  newmodulepath drop path nip
1386  2dup to newmodulepath
1387  modulepath setenv
1388
1389  \ Try all default kernel names
1390  flags args 1- load_a_kernel
1391  if ( failed once more )
1392    oldmodulepath restoreenv
1393    newmodulepath drop free-memory
1394    1
1395  else
1396    oldmodulepath nip -1 <> if
1397      newmodulepath s" ;" strcat
1398      oldmodulepath strcat
1399      modulepath setenv
1400      newmodulepath drop free-memory
1401      oldmodulepath drop free-memory
1402    then
1403    0
1404  then
1405;
1406
1407\ Try to load a kernel; the kernel name is taken from one of
1408\ the following lists, as ordered:
1409\
1410\   1. The "bootfile" environment variable
1411\   2. The "kernel" environment variable
1412\   3. The "path" argument
1413\
1414\ Flags are passed, if provided.
1415\
1416\ The kernel will be loaded from a directory computed from the
1417\ path given. Two directories will be tried in the following order:
1418\
1419\   1. /boot/path
1420\   2. path
1421\
1422\ Unless "path" is meant to be kernel name itself. In that case, it
1423\ will first be tried as a full path, and, next, search on the
1424\ directories pointed by module_path.
1425\
1426\ The module_path variable is overridden if load is successful, by
1427\ prepending the successful path.
1428
1429: load_directory_or_file ( path len 1 | flags len' path len 2 -- flag )
1430  local args
1431  2local path
1432  args 1 = if 0 0 then
1433  2local flags
1434  end-locals
1435
1436  \ First, assume path is an absolute path to a directory
1437  flags path args clip_args load_from_directory
1438  dup 0= if exit else drop then
1439
1440  \ Next, assume path points to the kernel
1441  flags path args try_multiple_kernels
1442;
1443
1444: initialize  ( addr len -- )
1445  strdup conf_files strset
1446;
1447
1448: kernel_options ( -- addr len 1 | 0 )
1449  s" kernel_options" getenv
1450  dup -1 = if drop 0 else 1 then
1451;
1452
1453: standard_kernel_search  ( flags 1 | 0 -- flag )
1454  local args
1455  args 0= if 0 0 then
1456  2local flags
1457  s" kernel" getenv
1458  dup -1 = if 0 swap then
1459  2local path
1460  end-locals
1461
1462  path nip -1 = if ( there isn't a "kernel" environment variable )
1463    flags args load_a_kernel
1464  else
1465    flags path args 1+ clip_args load_directory_or_file
1466  then
1467;
1468
1469: load_kernel  ( -- ) ( throws: abort )
1470  kernel_options standard_kernel_search
1471  abort" Unable to load a kernel!"
1472;
1473
1474: load_xen ( -- flag )
1475  s" xen_kernel" getenv dup -1 <> if
1476    1 1 load ( c-addr/u flag N -- flag )
1477  else
1478    drop
1479    0 ( -1 -- flag )
1480  then
1481;
1482
1483: load_xen_throw ( -- ) ( throws: abort )
1484  load_xen
1485  abort" Unable to load Xen!"
1486;
1487
1488: set_defaultoptions  ( -- )
1489  s" kernel_options" getenv dup -1 = if
1490    drop
1491  else
1492    s" temp_options" setenv
1493  then
1494;
1495
1496\ pick the i-th argument, i starts at 0
1497: argv[]  ( aN uN ... a1 u1 N i -- aN uN ... a1 u1 N ai+1 ui+1 )
1498  2dup = if 0 0 exit then	\ out of range
1499  dup >r
1500  1+ 2* ( skip N and ui )
1501  pick
1502  r>
1503  1+ 2* ( skip N and ai )
1504  pick
1505;
1506
1507: drop_args  ( aN uN ... a1 u1 N -- )
1508  0 ?do 2drop loop
1509;
1510
1511: argc
1512  dup
1513;
1514
1515: queue_argv  ( aN uN ... a1 u1 N a u -- a u aN uN ... a1 u1 N+1 )
1516  >r
1517  over 2* 1+ -roll
1518  r>
1519  over 2* 1+ -roll
1520  1+
1521;
1522
1523: unqueue_argv  ( aN uN ... a1 u1 N -- aN uN ... a2 u2 N-1 a1 u1 )
1524  1- -rot
1525;
1526
1527\ compute the length of the buffer including the spaces between words
1528: strlen(argv) ( aN uN .. a1 u1 N -- aN uN .. a1 u1 N len )
1529  dup 0= if 0 exit then
1530  0 >r	\ Size
1531  0 >r	\ Index
1532  begin
1533    argc r@ <>
1534  while
1535    r@ argv[]
1536    nip
1537    r> r> rot + 1+
1538    >r 1+ >r
1539  repeat
1540  r> drop
1541  r>
1542;
1543
1544: concat_argv  ( aN uN ... a1 u1 N -- a u )
1545  strlen(argv) allocate if ENOMEM throw then
1546  0 2>r ( save addr 0 on return stack )
1547
1548  begin
1549    dup
1550  while
1551    unqueue_argv ( ... N a1 u1 )
1552    2r> 2swap	 ( old a1 u1 )
1553    strcat
1554    s"  " strcat ( append one space ) \ XXX this gives a trailing space
1555    2>r		( store string on the result stack )
1556  repeat
1557  drop_args
1558  2r>
1559;
1560
1561: set_tempoptions  ( addrN lenN ... addr1 len1 N -- addr len 1 | 0 )
1562  \ Save the first argument, if it exists and is not a flag
1563  argc if
1564    0 argv[] drop c@ [char] - <> if
1565      unqueue_argv 2>r  \ Filename
1566      1 >r		\ Filename present
1567    else
1568      0 >r		\ Filename not present
1569    then
1570  else
1571    0 >r		\ Filename not present
1572  then
1573
1574  \ If there are other arguments, assume they are flags
1575  ?dup if
1576    concat_argv
1577    2dup s" temp_options" setenv
1578    drop free if EFREE throw then
1579  else
1580    set_defaultoptions
1581  then
1582
1583  \ Bring back the filename, if one was provided
1584  r> if 2r> 1 else 0 then
1585;
1586
1587: get_arguments ( -- addrN lenN ... addr1 len1 N )
1588  0
1589  begin
1590    \ Get next word on the command line
1591    parse-word
1592  ?dup while
1593    queue_argv
1594  repeat
1595  drop ( empty string )
1596;
1597
1598: load_kernel_and_modules  ( args -- flag )
1599  set_tempoptions
1600  argc >r
1601  s" temp_options" getenv dup -1 <> if
1602    queue_argv
1603  else
1604    drop
1605  then
1606  load_xen
1607  ?dup 0= if ( success )
1608    r> if ( a path was passed )
1609      load_directory_or_file
1610    else
1611      standard_kernel_search
1612    then
1613    ?dup 0= if ['] load_modules catch then
1614  then
1615;
1616
1617only forth definitions
1618