1########################################################################
2##
3## Copyright (C) 2009-2021 The Octave Project Developers
4##
5## See the file COPYRIGHT.md in the top-level directory of this
6## distribution or <https://octave.org/copyright/>.
7##
8## This file is part of Octave.
9##
10## Octave is free software: you can redistribute it and/or modify it
11## under the terms of the GNU General Public License as published by
12## the Free Software Foundation, either version 3 of the License, or
13## (at your option) any later version.
14##
15## Octave is distributed in the hope that it will be useful, but
16## WITHOUT ANY WARRANTY; without even the implied warranty of
17## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
18## GNU General Public License for more details.
19##
20## You should have received a copy of the GNU General Public License
21## along with Octave; see the file COPYING.  If not, see
22## <https://www.gnu.org/licenses/>.
23##
24########################################################################
25
26## -*- texinfo -*-
27## @deftypefn  {} {[@var{retval}, @var{status}] =} __makeinfo__ (@var{text}, @var{output_type})
28## @deftypefnx {} {[@var{retval}, @var{status}] =} __makeinfo__ (@var{text}, @var{output_type}, @var{fsee_also})
29## Undocumented internal function.
30## @end deftypefn
31
32## Run @code{makeinfo} on a given text.
33##
34## The string @var{text} is run through the @code{__makeinfo__} program
35## to generate output in various formats.  This string must contain valid
36## Texinfo formatted text.
37##
38## The @var{output_type} selects the format of the output.  This can be either
39## @t{"html"}, @t{"texinfo"}, or @t{"plain text"}.  By default this is
40## @t{"plain text"}.  If @var{output_type} is @t{"texinfo"}, the @t{@@seealso}
41## macro is expanded, but otherwise the text is unaltered.
42##
43## If the optional argument @var{see_also} is present, it is used to expand the
44## Octave specific @t{@@seealso} macro.  This argument must be a function
45## handle, that accepts a cell array of strings as input argument (each
46## elements of the array corresponds to the arguments to the @t{@@seealso}
47## macro), and return the expanded string.  If this argument is not given, the
48## @t{@@seealso} macro will be expanded to the text
49##
50## @example
51## See also: arg1, arg2, ...
52## @end example
53##
54## @noindent
55## for @t{"plain text"} output, and
56##
57## @example
58## See also: @@ref@{arg1@}, @@ref@{arg2@}, ...
59## @end example
60##
61## @noindent
62## otherwise.
63##
64## The optional output argument @var{status} contains the exit status of the
65## @code{makeinfo} program as returned by @code{system}.
66
67function [retval, status] = __makeinfo__ (text, output_type = "plain text", fsee_also)
68
69  ## Check input
70  if (nargin < 1 || nargin > 3)
71    print_usage ();
72  endif
73
74  if (! ischar (text))
75    error ("__makeinfo__: first input argument must be a string");
76  endif
77
78  if (! ischar (output_type))
79    error ("__makeinfo__: second input argument must be a string");
80  endif
81
82  ## NOTE: The 3rd argument is used by Octave Forge function
83  ##       generate_package_html, not by core Octave.  This functionality
84  ##       can only be removed when that function has been updated.
85  if (nargin < 3)
86    if (strcmpi (output_type, "plain text"))
87      fsee_also = @(T) ["\nSee also:", sprintf(" %s,", T{:})(1:end-1), "\n"];
88    else
89      fsee_also = @(T) ["\nSee also:", sprintf(" @ref{%s},", T{:})(1:end-1), "\n"];
90    endif
91  endif
92
93  if (! is_function_handle (fsee_also))
94    error ("__makeinfo__: third input argument must be a function handle");
95  endif
96
97  ## Formatting in m-files has an extra space at the beginning of every line.
98  ## Remove these unwanted spaces if present.  First text char is "\n" delim.
99  if (text(2) == " ")
100    text = strrep (text, "\n ", "\n");
101  endif
102  ## Texinfo crashes if @end tex does not appear first on the line.
103  text = regexprep (text, '^ +@end tex', '@end tex', 'lineanchors');
104  text = regexprep (text, '@seealso', '@xseealso');
105
106  ## We don't want *ref macros to clutter plain text output with "Note ..."
107  if (strcmp (output_type, "plain text"))
108    text  = regexprep (text, '@ref{(?:[^}]*?),?(?:XREF)?([^,}]+)}', '$1');
109    text  = regexprep (text, '@xref{(?:[^}]*?),?(?:XREF)?([^,}]+)}', 'See $1');
110    text  = regexprep (text, '@pxref{(?:[^}]*?),?(?:XREF)?([^,}]+)}', 'see $1');
111  endif
112
113  file = texi_macros_file ();
114  fid = fopen (file, "r");
115  if (fid < 0)
116    error ("unable to open %s for reading", file);
117  else
118    macros_text = fread (fid, Inf, "*char")';
119    text = [macros_text text];
120  endif
121  fclose (fid);
122
123  if (strcmpi (output_type, "texinfo"))
124    status = 0;
125    retval = text;
126    return;
127  endif
128
129  ## Create the final TeXinfo input string
130  text = sprintf ("\\input texinfo\n\n%s\n\n@bye\n", text);
131
132  unwind_protect
133    ## Write Texinfo to tmp file
134    template = "octave-help-XXXXXX";
135    [fid, name] = mkstemp (fullfile (tempdir, template), true);
136    if (fid < 0)
137      error ("__makeinfo__: could not create temporary file");
138    endif
139    fprintf (fid, "%s", text);
140    fclose (fid);
141
142    ## Take action depending on output type
143    switch (lower (output_type))
144      case "plain text"
145        cmd = sprintf ('%s --no-headers --no-warn --no-validate --plaintext --output=- "%s"',
146                       makeinfo_program (), name);
147      case "html"
148        cmd = sprintf ('%s --no-headers --html --no-warn --no-validate --output=- "%s"',
149                       makeinfo_program (), name);
150      otherwise
151        error ("__makeinfo__: unsupported output type: '%s'", output_type);
152    endswitch
153
154    ## Call makeinfo
155    [status, retval] = system (cmd);
156
157    ## On error, retry with force to ensure user gets *something*
158    if (status)
159      cmd = regexprep (cmd, '--output=', '--force --output=', 'once');
160      [status_force, retval] = system (cmd);
161      ## original return value usually more useful
162      if (status_force)
163        status = status_force;
164      end
165    end
166
167    ## Clean up extra newlines generated by makeinfo
168    if (strcmpi (output_type, "plain text"))
169      if (numel (retval) > 2 && retval(end-1:end) == "\n\n")
170        retval = retval(1:end-2);
171      endif
172    endif
173
174    ## Clean up start of @deftypefn expansion which includes extra ':'
175    retval = regexprep (retval, '^ -- : +', ' -- ', "lineanchors");
176
177  unwind_protect_cleanup
178    if (exist (name, "file"))
179      delete (name);
180    endif
181  end_unwind_protect
182
183endfunction
184
185
186## No test needed for internal helper function.
187%!assert (1)
188