1%_debug_info = 1;
2%
3% Info reader for JED
4%
5
6variable Info_This_Filename = Null_String;
7variable Info_This_Filedir = Null_String;
8
9#ifndef VMS
10% returns compression extension if file is compressed or "" if not
11define info_is_compressed (file)
12{
13   variable exts, ext, n;
14   exts = ".Z,.z,.gz,.bz2";
15   n = 0;
16   forever
17     {
18	ext = extract_element(exts, n, ',');
19	if (ext == NULL) return "";
20
21	if (1 == file_status(file + ext)) break;
22	n++;
23     }
24   ext;
25}
26#endif
27
28
29define info_make_file_name (file)
30{
31   variable n, dir, dirfile, df, df_low;
32   variable cext = ""; % compressed extension
33
34   n = 0;
35   forever
36     {
37        %
38        % Try to find requested file in remembered directory.
39        %
40	dirfile = expand_filename(dircat(Info_This_Filedir, file));
41	if (1 == file_status(dirfile)) break;
42
43	dir = extract_element(Info_Directory, n, ',');
44	if (dir == NULL) dir = "";
45	df = expand_filename(dircat(dir,file));
46
47	% try with first with info extension
48#ifdef VMS
49	dirfile = df + "info";  % VMS adds a '.' upon expansion
50#else
51	dirfile = df + ".info";
52#endif
53
54
55	if (1 == file_status(dirfile)) break;
56#ifndef VMS
57	cext = info_is_compressed(dirfile);
58	if (strlen(cext)) break;
59#endif
60	df_low = expand_filename(dircat(dir,strlow(file)));
61
62#ifdef VMS
63	dirfile = df_low + "info";  % VMS adds a '.' upon expansion
64#else
65	dirfile = df_low + ".info";
66#endif
67
68	if (1 == file_status(dirfile)) break;
69#ifndef VMS
70	cext = info_is_compressed(dirfile);
71	if (strlen(cext)) break;
72#endif
73
74 	% try next with inf extension, since .info causes problems on FAT
75	% In addition, Unix and VMS distributions may have been derived from
76	% PC 8+3 distributions.
77	%
78	% Also Windows 95 supports long filenames.  Since that OS is also
79	% considered to be MSDOS, we need to try this for MSDOS as well
80	% even though it has no effect under a true MSDOS system.
81 	dirfile = df_low + ".inf";
82
83 	if (1 == file_status(dirfile)) break;
84#ifndef VMS
85 	cext = info_is_compressed(dirfile);
86 	if (strlen(cext)) break;
87#endif
88
89% repeat without extension
90
91	dirfile = df;
92
93	if (1 == file_status(dirfile)) break;
94#ifndef VMS
95	cext = info_is_compressed(dirfile);
96	if (strlen(cext)) break;
97#endif
98	dirfile = df_low;
99	if (1 == file_status(dirfile)) break;
100#ifdef UNIX
101	cext = info_is_compressed(dirfile);
102	if (strlen(cext)) break;
103#endif
104
105	!if (strlen(dir)) error ("Info file not found: " + file);
106	++n;
107     }
108
109   (Info_This_Filedir, ) = parse_filename(dirfile);
110
111   return (dirfile, cext);
112}
113
114private define make_unzip_cmd (ext)
115{
116   variable unzip_cmd = "uncompress -c";
117   switch (ext)
118     {
119      case ".gz":
120	unzip_cmd = "gzip -dc";
121     }
122     {
123      case ".bz2":
124	unzip_cmd = "bzip2 -dc";
125     }
126   return unzip_cmd;
127}
128
129
130define info_find_file (file)
131{
132   variable dirfile, flags, buf, dir;
133   variable ext;
134
135   (dirfile, ext) = info_make_file_name(file);
136
137   setbuf("*Info*");
138   set_readonly(0);
139   widen(); erase_buffer();
140
141#ifndef VMS
142   if (strlen(ext))
143     () = run_shell_cmd (sprintf("%s %s%s", make_unzip_cmd (ext), dirfile, ext));
144   else
145#endif
146     () = insert_file(dirfile);
147
148   bob();
149   Info_This_Filename = dirfile;
150   set_readonly(1);
151   set_buffer_modified_flag(0);
152   set_mode("Info", 1);
153   use_keymap("Infomap");
154   set_status_line(" Jed Info: %f   (%m%n)   Press '?' for help.    (%p)", 0);
155
156   % The following 2 lines will cause problems when the buffer attached to
157   % the specified file is erased from a re-only directory.
158   %( , dir, buf, flags) = getbuf_info();
159   %setbuf_info(extract_filename(Info_This_Filename), dir, buf, flags);
160}
161
162
163define info_find_node_split_file();  % extern
164
165variable Info_Split_File_Buffer;  Info_Split_File_Buffer = Null_String;
166variable Info_Split_Filename;  Info_Split_Filename = Null_String;
167
168define info_search_marker(dir)
169{
170   variable mark, pnt, search_fun;
171   mark = "\x1F";
172   if (dir > 0) search_fun = &fsearch; else search_fun = &bsearch;
173   push_mark();
174   forever
175     {
176	if (not(@search_fun(mark)))
177	  {
178	     pop_mark_1 ();
179	     return(0);
180	  }
181	if (bolp()) break;
182	pnt = _get_point ();
183	bol(); skip_chars("\x01-\x1E ");
184	go_right_1 ();
185	pnt = _get_point () - pnt;
186	if ((pnt == 1) and (eolp() or looking_at_char('\xC'))) break;
187	if (dir > 0) eol(); else bol();
188     }
189   pop_mark_0 ();
190   return (1);
191}
192
193define info_find_node_this_file (the_node)
194{
195   variable node, len, fnd;
196   CASE_SEARCH = 0;
197   node = "Node: " + the_node;
198   len = strlen(node);
199   widen(); bob();
200   forever
201     {
202	% some of this could/should be replaced by a regular expression:
203	% !if (re_fsearch("^[\t ]*\x1F")) ....
204
205	!if (info_search_marker(1))
206	  {
207	     % dont give up, maybe this is a split file
208	     !if (strlen(Info_Split_File_Buffer))
209	     error("Marker not found. " + node);
210	     setbuf(Info_Split_File_Buffer);
211	     info_find_node_split_file(the_node);
212	     return;
213	  }
214	go_down_1 (); % bol();  --- already implicit
215	if (ffind(node))
216	  {
217	     % is this really it?  ---
218	     go_right(len);
219	     if (eolp() or looking_at_char(',') or looking_at_char('\t')) break;
220	  }
221
222	eol ();
223     }
224
225   push_mark();
226   if (info_search_marker(1)) go_up_1(); else eob();
227   narrow();
228   bob();
229}
230
231
232define info_find_node_split_file (node)
233{
234   variable tag, tagpos, pos, pos_len, tag_len, buf, file;
235   variable re;
236   buf = " *Info*";
237
238   !if (bufferp(buf), setbuf(buf))
239     {
240	insbuf("*Info*");
241     }
242
243   widen();
244
245   % make this re safe
246   tag = str_quote_string (node, "\\^$[]*.+?", '\\');
247
248   tag = "Node: " + tag;
249   eob();
250
251
252   %!if (bol_bsearch(tag)) error("tag not found.");
253   %go_right(strlen(tag));
254   %skip_chars(" \t\x7F");
255
256   re = tag + "[\t \x7F]\\d+[ \t]*$";
257
258   !if (re_bsearch(re)) verror ("tag %s not found.", tag);
259   eol ();
260   bskip_chars(" \t");
261   push_mark(); bskip_chars ("0-9");
262   tagpos = bufsubstr();  % see comment about DOS below
263   tag_len = strlen(tagpos);
264
265   bob ();
266   bol_fsearch("Indirect:"); pop();
267   push_mark();
268   !if (info_search_marker(1)) eob();
269   narrow();
270   bob();
271   forever
272     {
273	!if (down_1 ()) break;
274	% bol(); --- implicit in down
275	!if (ffind(": ")) break;
276	go_right(2);
277
278	% This will not work on DOS with 16 bit ints.  Do strcmp instead.
279	push_mark_eol(); pos = bufsubstr();
280	pos_len = strlen(pos);
281	if (tag_len > pos_len) continue;
282	if (tag_len < pos_len) break;
283	% now ==
284	if (strcmp(tagpos, pos) < 0) break;
285     }
286
287   Info_Split_File_Buffer = Null_String;
288   go_up_1 (); bol();
289   push_mark();
290   () = ffind(": ");
291   widen();
292   file = bufsubstr();
293
294   info_find_file(file);
295   info_find_node_this_file(node);
296   Info_Split_File_Buffer = buf;
297}
298
299
300
301define info_narrow()
302{
303   if (whatbuf () != "*Info*") return;
304   push_spot();  push_spot();
305   () = info_search_marker(-1);
306   go_down_1 (); push_mark();
307   pop_spot();
308   if (info_search_marker(1)) go_up_1 (); else eob();
309   narrow();
310   pop_spot();
311}
312
313
314  % stack for last position
315
316!if (is_defined ("Info_Position_Type"))
317{
318   typedef struct
319     {
320	filename,
321	split_filename,
322	line_number
323     }
324   Info_Position_Type;
325}
326
327
328variable Info_Position_Stack = Info_Position_Type [16];
329variable Info_Stack_Depth = 0;
330
331define info_push_position(file, split, line)
332{
333   variable i;
334   variable pos;
335
336   if (Info_Stack_Depth == 16)
337     {
338        --Info_Stack_Depth;
339	for (i = 1; i < 16; i++)
340	  Info_Position_Stack [i - 1] = @Info_Position_Stack [i];
341     }
342
343   pos = Info_Position_Stack [Info_Stack_Depth];
344
345   pos.filename = file;
346   pos.split_filename = split;
347   pos.line_number = line;
348
349   ++Info_Stack_Depth;
350}
351
352
353define info_record_position ()
354{
355   variable i, file;
356
357   if (whatbuf() != "*Info*") return;
358   widen();
359   file = Null_String;
360
361   if (strlen (Info_Split_File_Buffer)) file = Info_Split_Filename;
362   info_push_position(Info_This_Filename, file, what_line());
363   info_narrow();
364}
365
366
367
368
369define info_find_node(node)
370{
371   variable the_node, file, n, len;
372   n = 0;
373
374   % Replace \n and \t characters in name by spaces
375   node = strcompress (node, " \t\n");
376
377   info_record_position();
378   ERROR_BLOCK
379     {
380	if (bufferp ("*Info*"))
381	  info_reader ();
382     }
383
384   len = strlen(node);
385  % if it looks like (file)node, extract file, node
386
387   if (is_substr(node, "(") == 1) n = is_substr(node, ")");
388
389   if (n)
390     {
391	variable save_node = "|" + node + "|";
392	the_node = node;
393	node = substr(the_node, n + 1, strlen(node));
394	the_node = strsub(the_node, n, 0);  % truncate string
395	file = substr(the_node, 2, n-2);
396	if (bufferp(Info_Split_File_Buffer)) delbuf(Info_Split_File_Buffer);
397	Info_Split_File_Buffer = Null_String;
398	info_find_file(file);
399     }
400
401   node = strtrim (node);
402   !if (strlen(node)) node = "Top";
403   widen();
404   push_spot_bob ();
405   !if (info_search_marker(1)) error("Marker not found.");
406   go_down_1 ();
407
408   if (looking_at("Indirect:"), pop_spot())
409     {
410	Info_Split_Filename = Info_This_Filename;
411	info_find_node_split_file(node);
412     }
413   else info_find_node_this_file(node);
414}
415
416
417% If buffer has a menu, point is put on line after menu marker if argument
418% is non-zero, otherwise leave point as is.
419% signals error if no menu.
420define info_find_menu(save)
421{
422   variable menu_re = "^\\c\\* Menu:";
423   push_spot();
424
425   bob ();
426
427   forever
428     {
429	!if (re_fsearch(menu_re))
430	  {
431	     pop_spot();
432	     error ("Node has no menu.");
433	  }
434
435	go_right (7);
436	!if (looking_at_char (':'))
437	  break;
438     }
439
440   !if (save)
441     {
442	pop_spot();
443	return;
444     }
445
446   eol(); go_right_1 ();
447   push_mark(); pop_spot(); pop_mark_1 ();
448}
449
450
451
452% Move move the cursor to the start of the next nearest menu item or
453% note reference in this node if possible.
454%
455define info_next_xref ()
456{
457   push_mark (); go_right_1 ();
458   if (re_fsearch("\\*.*:")) exchange_point_and_mark ();
459   pop_mark_1 ();
460}
461%
462% Move move the cursor to the start of the previous nearest menu item or
463% note reference in this node if possible.
464%
465define info_prev_xref ()
466{
467   push_mark (); go_left_1 ();
468   if (re_bsearch("[*].*:")) exchange_point_and_mark ();
469   pop_mark_1 ();
470}
471
472
473% menu references
474
475define info_follow_current_xref ()
476{
477   variable node;
478
479   push_spot();
480
481   !if (fsearch_char (':'))
482     {
483	pop_spot(); error ("Corrupt File?");
484     }
485
486   if (looking_at("::"))
487     {
488        push_mark();
489        pop_spot();
490        node = bufsubstr();
491     }
492   else
493     {
494        go_right_1 ();
495        skip_white();
496	if (eolp())
497	  {
498	     go_right_1 ();
499	     skip_white();
500	  }
501        push_mark();
502	if (looking_at_char('(')) () = ffind_char (')');
503	% comma, tab, '.', or newline terminates
504	skip_chars ("^,.\t\n");
505	%skip_chars("\d032-\d043\d045\d047-\d255");
506
507        bskip_chars(" ");
508	node = bufsubstr(());
509        pop_spot();
510     }
511   info_find_node(node);
512}
513
514define info_menu ()
515{
516   variable node, colons, colon;
517   node = Null_String;
518   colon = ":"; colons = "::";
519
520   if ((LAST_CHAR == '\r') and re_looking_at ("\\C*Note[ \t\n]"))
521     %and not(bolp ()))
522     {
523	go_right (5); skip_chars (" \t\n");
524	info_follow_current_xref ();
525	return;
526     }
527
528   info_find_menu (0);
529
530   bol ();
531
532   if (looking_at("* ")
533       and (ffind(colon)))
534     {
535	push_mark();
536	bol(); go_right(2);
537	node = bufsubstr() + colon;
538	bol ();
539     }
540
541   !if (strlen (node) and (LAST_CHAR == '\r'))
542     {
543	node = read_mini("Menu item:", node, Null_String);
544	info_find_menu (1);
545     }
546
547   !if (bol_fsearch("* " + node)) error ("Menu Item not found.");
548   !if (ffind(colon)) error ("Corrupt File?");
549
550   if (looking_at(colons))
551     {
552	push_mark();
553	bol(); go_right(2);
554     }
555   else
556     {
557        go_right_1 ();
558        skip_white();
559        push_mark();
560	if (looking_at_char('('))
561	  {
562	     () = ffind_char (')');
563	  }
564	% comma, tab, '.', or newline terminates
565	skip_chars ("^,.\t\n");
566	%skip_chars("\d032-\d043\d045\d047-\d255");
567
568        bskip_chars(" ");
569     }
570   info_find_node(bufsubstr(()));
571}
572
573define info_find_dir()
574{
575   info_find_node ("(DIR)top");
576}
577
578
579
580define info_up ()
581{
582   bob();
583   !if (ffind("Up: "))
584     {
585	info_find_dir ();
586	return;
587     }
588
589   go_right(4); push_mark();
590   % comma, tab, or newline terminates
591   skip_chars ("^,.\t\n");
592   %skip_chars("\d032-\d043\d045-\d255");
593   bskip_chars(" ");
594   info_find_node(bufsubstr(()));
595}
596
597define info_prev()
598{
599   variable n;  n = 10;
600   bob();
601   !if (ffind("Previous: "))
602     {
603	!if (ffind("Prev: ")) error ("Node has no PREVIOUS");
604	n = 6;
605     }
606
607   go_right(n); push_mark();
608   skip_chars ("^,.\t\n");
609   %skip_chars("\d032-\d043\d045-\d255");
610   bskip_chars(" ");
611   info_find_node(bufsubstr(()));
612}
613
614
615define info_goto_last_position ()
616{
617   variable split_file, file, n;
618   variable pos;
619
620   if (Info_Stack_Depth == 0) return;
621
622   --Info_Stack_Depth;
623
624   pos = Info_Position_Stack [Info_Stack_Depth];
625
626   split_file = pos.split_filename;
627   file = pos.filename;
628   n = pos.line_number;
629
630   if ((file == Info_This_Filename) and bufferp("*Info*"))
631     {
632        widen();
633        goto_line(n); bol();
634        info_narrow();
635        return;
636     }
637
638   if (strlen(split_file))
639     {
640	setbuf(" *Info*");
641	set_readonly(0);
642	widen();
643	erase_buffer();
644#ifndef VMS
645 	variable ext = info_is_compressed (split_file);
646 	if (strlen(ext))
647	  () = run_shell_cmd(sprintf("%s %s%s", make_unzip_cmd (ext), split_file, ext));
648	else
649#endif
650	  () = insert_file (split_file);
651
652	Info_Split_File_Buffer = whatbuf ();
653	setbuf ("*Info*");
654     }
655
656   !if (strlen(file)) return;
657   info_find_file(file);
658   goto_line(n); bol();
659   info_narrow();
660}
661
662define info_next ()
663{
664   bob();
665   !if (ffind("Next: "))
666     {
667	info_goto_last_position ();
668	message ("Node has no NEXT.");
669	return;
670     }
671   go_right(6); push_mark();
672   % comma, tab, or newline terminates
673   skip_chars ("^,.\t\n");
674   %skip_chars("\d032-\d043\d045-\d255");
675   bskip_chars(" ");
676   info_find_node(bufsubstr(()));
677}
678
679define info_quick_help()
680{
681  message("q:quit,  h:tutorial,  SPC:next screen,  DEL:prev screen,  m:menu,  s:search");
682}
683
684
685$2 = "Infomap";
686!if (keymap_p($2))
687{
688   make_keymap($2);
689   definekey("info_quick_help",		"?", $2);
690   definekey("info_tutorial",		"h", $2);
691   definekey("info_tutorial",		"H", $2);
692   definekey("info_menu",		"^M", $2);
693   definekey("info_menu",		"M", $2);
694   definekey("info_menu",		"m", $2);
695
696   definekey("info_next_xref",		"\t", $2);
697#ifdef MSDOS MSWINDOWS
698   definekey("info_prev_xref",		"^@^O", $2);
699#endif
700
701   definekey("info_next",		"N", $2);
702   definekey("info_next",		"n", $2);
703   definekey("info_prev",		"P", $2);
704   definekey("info_prev",		"p", $2);
705   definekey("info_up",			"U", $2);
706   definekey("info_up",			"u", $2);
707   definekey("page_down",		" ", $2);
708   definekey("page_up",			"^?", $2);
709   definekey("bob",			"B",  $2);
710   definekey("bob",			"b",  $2);
711   definekey("info_goto_node",		"G", $2);
712   definekey("info_goto_node",		"g", $2);
713   definekey("info_quit",		"q",  $2);
714   definekey("info_quit",		"Q",  $2);
715   definekey("info_goto_last_position",	"l",  $2);
716   definekey("info_goto_last_position",	"L",  $2);
717   definekey("info_search",		"S",  $2);
718   definekey("info_search",		"s",  $2);
719   definekey("info_search",		"/",  $2);
720   definekey("info_follow_reference",	"f",  $2);
721   definekey("info_follow_reference",	"F",  $2);
722   definekey("info_find_dir",		"D",  $2);
723   definekey("info_find_dir",		"d",  $2);
724   _for (1, 9, 1)
725     {
726	$1 = ();
727	definekey("info_menu_number", string($1), $2);
728     }
729}
730
731
732define info_quit ()
733{
734   info_record_position();
735   widen();
736   delbuf("*Info*");
737}
738
739
740define info_goto_node()
741{
742   info_find_node (read_mini("Node:", Null_String, Null_String));
743}
744
745
746define info_search ()
747{
748   variable this_line, this_file, str, err_str, file, wline, ifile, ext;
749   err_str = "String not found.";
750
751   str = read_mini("Re-Search:", LAST_SEARCH, Null_String);
752   !if (strlen(str)) return;
753   save_search_string(str);
754   widen(); go_right_1 ();
755   if (re_fsearch(str))
756     {
757	info_narrow();
758	return;
759     }
760
761   %
762   %  Not found.  Look to see if this is split.
763   %
764   !if (strlen(Info_Split_File_Buffer))
765     {
766	info_narrow();
767	error (err_str);
768     }
769
770   this_file = Info_This_Filename;
771   this_line = what_line();
772   wline = window_line(); %need this so state can be restored after a failure.
773
774
775   setbuf(Info_Split_File_Buffer); widen(); bob();
776   bol_fsearch("Indirect:"); pop();
777   push_mark();
778   if (info_search_marker(1)) go_up_1 (); else eob();
779   narrow();
780   bob();
781   bol_fsearch(extract_filename(this_file)); pop();
782
783   ERROR_BLOCK
784     {
785	widen();
786	info_find_file (this_file);
787	goto_line(this_line); eol();
788	info_narrow();
789	recenter(wline);
790     }
791
792   while (down_1 ())
793     {
794	% bol(); --- implicit
795	push_mark();
796
797	!if (ffind_char (':')) {pop_mark_0 ();  break; }
798	file = bufsubstr();
799	flush("Searching " + file);
800	(ifile, ext) = info_make_file_name(file);
801#ifdef UNIX OS2
802	if (strlen(ext))
803	  {
804	     variable re = str;
805
806	     % Not all greps support -e option.  So, try this:
807	     if (re[0] == '-') re = "\\" + re;
808
809	     setbuf(" *Info*zcat*"); erase_buffer();
810
811	     () = run_shell_cmd(sprintf("%s %s%s | grep -ci '%s'",
812					make_unzip_cmd (ext),
813					ifile, ext,
814					re));
815	     bob();
816	     if (looking_at_char ('0'))
817	       {
818		  delbuf(whatbuf());
819		  setbuf(Info_Split_File_Buffer);
820		  continue;
821	       }
822	     setbuf(Info_Split_File_Buffer);
823	  }
824	else
825#endif
826	!if (search_file(ifile, str, 1))
827	  {
828	     setbuf(Info_Split_File_Buffer);
829	     continue;
830	  }
831
832	info_find_file(file);
833	pop(fsearch(str));
834	info_narrow();
835	info_push_position(this_file, Info_Split_Filename, this_line);
836	return;
837     }
838   error (err_str);
839}
840
841define info_looking_at (ref)
842{
843   variable n;
844   variable word;
845
846   push_spot ();
847   EXIT_BLOCK
848     {
849	pop_spot ();
850     }
851   ref = strcompress (ref, " ");
852
853   n = 0;
854   while (word = extract_element (ref, n,  ' '), word != NULL)
855     {
856	n++;
857	skip_chars (" \t\n");
858	!if (looking_at (word)) return 0;
859	go_right (strlen (word));
860     }
861   1;
862}
863
864
865
866define info_follow_reference ()
867{
868   variable colon, colons, note, err, item, node, ref;
869   colon = ":"; colons = "::";
870   note = "*Note";
871   err = "No cross references.";
872
873   push_spot();
874   !if (fsearch(note))
875     {
876	!if (bsearch(note))
877	  {
878	     pop_spot();
879	     error(err);
880	  }
881     }
882   pop_spot();
883
884   ref = read_mini("Follow *Note", Null_String, Null_String);
885   push_spot_bob ();
886   forever
887     {
888	!if (fsearch(note))
889	  {
890	     pop_spot();
891	     error ("Bad reference.");
892	  }
893	go_right (5);  skip_chars (" \t\n");
894	% skip_white();
895	% if (eolp())
896	%  {
897	%     go_right_1 (); skip_white();
898	%  }
899	if (info_looking_at(ref)) break;
900     }
901
902   push_mark();
903   pop_spot();
904   %info_record_position
905   pop_mark_1 ();
906
907   info_follow_current_xref ();
908}
909
910
911
912define info_menu_number ()
913{
914   variable node;  node = Null_String;
915   variable colon, colons;
916   colons = "::"; colon = ":";
917   variable n;
918
919   n = LAST_CHAR;
920   if ((n < '1') or (n > '9')) return (beep());
921   n -= '0';
922
923   info_find_menu(1);
924
925   while (n)
926     {
927	!if (bol_fsearch("* ")) return (beep());
928	if (ffind(colon)) --n; else eol();
929     }
930
931   if (looking_at(colons))
932     {
933        push_mark();
934	bol(); go_right(2);
935     }
936   else
937     {
938	go_right_1 ();  skip_white();  push_mark();
939	if (looking_at_char('('))
940          {
941	     () = ffind_char (')');
942	  }
943	% comma, tab, '.', or newline terminates
944	skip_chars ("^,.\t\n");
945	%skip_chars("\d032-\d043\d045\d047-\d255");
946	bskip_chars(" ");
947     }
948   info_find_node(bufsubstr(()));
949}
950
951
952
953define info_tutorial()
954{
955   info_find_node("(info)help");
956}
957
958private define start_info_reader ()
959{
960   variable ibuf; ibuf = "*Info*";
961   if (Info_Stack_Depth) info_goto_last_position ();
962   !if (bufferp(ibuf)) info_find_dir();
963   pop2buf(ibuf);
964   onewindow();
965   if (0 == is_defined ("info_reader_hook"))
966     run_mode_hooks ("info_mode_hook");
967   else
968     run_mode_hooks ("info_reader_hook");
969}
970
971
972% Usage:
973%   info_reader ()
974%   info_reader (args);
975%     args[0] = file
976%     args[1] = node
977define info_reader ()
978{
979   variable file, node;
980
981   start_info_reader ();
982
983   if (_NARGS == 0)
984     return;
985
986   variable args = ();
987   variable nargs = length (args);
988
989   local_setkey ("exit_jed",		"q");
990   local_setkey ("exit_jed",		"Q");
991
992   if (nargs > 0)
993     {
994	file = args[0];
995
996#ifdef UNIX
997	if (path_basename (file) != file)
998	  {
999	     variable dir = path_dirname (file);
1000	     file = path_basename (file);
1001	     Info_Directory = strcat (dir, "," + Info_Directory);
1002	  }
1003#endif
1004	% Goto top incase the requested node does not exist.
1005	info_find_node (sprintf ("(%s)top", file));
1006	if (nargs > 1)
1007	  info_find_node (sprintf ("(%s)%s", file, args[1]));
1008     }
1009}
1010