]> Creatis software - CreaPhase.git/blob - octave_packages/m/miscellaneous/edit.m
update packages
[CreaPhase.git] / octave_packages / m / miscellaneous / edit.m
1 ## Copyright (C) 2001-2012 Paul Kienzle
2 ##
3 ## This file is part of Octave.
4 ##
5 ## Octave is free software; you can redistribute it and/or modify it
6 ## under the terms of the GNU General Public License as published by
7 ## the Free Software Foundation; either version 3 of the License, or (at
8 ## your option) any later version.
9 ##
10 ## Octave is distributed in the hope that it will be useful, but
11 ## WITHOUT ANY WARRANTY; without even the implied warranty of
12 ## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
13 ## General Public License for more details.
14 ##
15 ## You should have received a copy of the GNU General Public License
16 ## along with Octave; see the file COPYING.  If not, see
17 ## <http://www.gnu.org/licenses/>.
18
19 ## -*- texinfo -*-
20 ## @deftypefn  {Command} {} edit @var{name}
21 ## @deftypefnx {Command} {} edit @var{field} @var{value}
22 ## @deftypefnx {Command} {@var{value} =} edit get @var{field}
23 ## Edit the named function, or change editor settings.
24 ##
25 ## If @code{edit} is called with the name of a file or function as
26 ## its argument it will be opened in a text editor.
27 ##
28 ## @itemize @bullet
29 ## @item
30 ## If the function @var{name} is available in a file on your path and
31 ## that file is modifiable, then it will be edited in place.  If it
32 ## is a system function, then it will first be copied to the directory
33 ## @env{HOME} (see further down) and then edited.
34 ## If no file is found, then the m-file
35 ## variant, ending with ".m", will be considered.  If still no file
36 ## is found, then variants with a leading "@@" and then with both a
37 ## leading "@@" and trailing ".m" will be considered.
38 ##
39 ## @item
40 ## If @var{name} is the name of a function defined in the interpreter but
41 ## not in an m-file, then an m-file will be created in @env{HOME}
42 ## to contain that function along with its current definition.
43 ##
44 ## @item
45 ## If @code{name.cc} is specified, then it will search for @code{name.cc}
46 ## in the path and try to modify it, otherwise it will create a new
47 ## @file{.cc} file in @env{HOME}.  If @var{name} happens to be an
48 ## m-file or interpreter defined function, then the text of that
49 ## function will be inserted into the .cc file as a comment.
50 ##
51 ## @item
52 ## If @var{name.ext} is on your path then it will be edited, otherwise
53 ## the editor will be started with @file{HOME/name.ext} as the
54 ## filename.  If @file{name.ext} is not modifiable, it will be copied to
55 ## @env{HOME} before editing.
56 ##
57 ## @strong{Warning:} You may need to clear name before the new definition
58 ## is available.  If you are editing a .cc file, you will need
59 ## to mkoctfile @file{name.cc} before the definition will be available.
60 ## @end itemize
61 ##
62 ## If @code{edit} is called with @var{field} and @var{value} variables,
63 ## the value of the control field @var{field} will be @var{value}.
64 ## If an output argument is requested and the first argument is @code{get}
65 ## then @code{edit} will return the value of the control field @var{field}.
66 ## If the control field does not exist, edit will return a structure
67 ## containing all fields and values.  Thus, @code{edit get all} returns
68 ## a complete control structure.
69 ## The following control fields are used:
70 ##
71 ## @table @samp
72 ## @item editor
73 ## This is the editor to use to modify the functions.  By default it uses
74 ## Octave's @env{EDITOR} built-in function, which comes from
75 ## @code{getenv("EDITOR")} and defaults to @code{emacs}.  Use @code{%s}
76 ## In place of the function name.  For example,
77 ## @table @samp
78 ## @item [EDITOR, " %s"]
79 ## Use the editor which Octave uses for @code{edit_history}.
80 ##
81 ## @item "xedit %s &"
82 ## pop up simple X11 editor in a separate window
83 ##
84 ## @item "gnudoit -q \"(find-file \\\"%s\\\")\""
85 ## Send it to current Emacs; must have @code{(gnuserv-start)} in @file{.emacs}.
86 ## @end table
87 ##
88 ## See also field 'mode', which controls how the editor is run by Octave.
89 ##
90 ## On Cygwin, you will need to convert the Cygwin path to a Windows
91 ## path if you are using a native Windows editor.  For example:
92 ## @c Set example in small font to prevent overfull line in TeX
93 ##
94 ## @smallexample
95 ## @exdent '"C:/Program Files/Good Editor/Editor.exe" "$(cygpath -wa %s)"'
96 ## @end smallexample
97 ##
98 ## @item home
99 ## This is the location of user local m-files.  Be be sure it is in your
100 ## path.  The default is @file{~/octave}.
101 ##
102 ## @item author
103 ## This is the name to put after the "## Author:" field of new functions.
104 ## By default it guesses from the @code{gecos} field of password database.
105 ##
106 ## @item email
107 ## This is the e-mail address to list after the name in the author field.
108 ## By default it guesses @code{<$LOGNAME@@$HOSTNAME>}, and if @code{$HOSTNAME}
109 ## is not defined it uses @code{uname -n}.  You probably want to override this.
110 ## Be sure to use @code{<user@@host>} as your format.
111 ##
112 ## @item license
113 ## @table @samp
114 ## @item gpl
115 ## GNU General Public License (default).
116 ##
117 ## @item bsd
118 ## BSD-style license without advertising clause.
119 ##
120 ## @item pd
121 ## Public domain.
122 ##
123 ## @item "text"
124 ## Your own default copyright and license.
125 ## @end table
126 ##
127 ## Unless you specify @samp{pd}, edit will prepend the copyright statement
128 ## with "Copyright (C) yyyy Function Author".
129 ##
130 ## @item mode
131 ## This value determines whether the editor should be started in async mode
132 ## (editor is started in the background and Octave continues) or sync mode
133 ## (Octave waits until the editor exits).  Set it to "sync" to start the editor
134 ## in sync mode.  The default is "async" (see also "system").
135 ##
136 ## @item editinplace
137 ## Determines whether files should be edited in place, without regard to
138 ## whether they are modifiable or not.  The default is @code{false}.
139 ## @end table
140 ## @end deftypefn
141
142 ## Author: Paul Kienzle <pkienzle@users.sf.net>
143
144 ## Original version by Paul Kienzle distributed as free software in the
145 ## public domain.
146
147 function ret = edit (file, state)
148
149   ## Pick up globals or default them.
150
151   persistent FUNCTION = struct ("EDITOR", cstrcat (EDITOR (), " %s"),
152                                 "HOME", fullfile (default_home, "octave"),
153                                 "AUTHOR", default_user(1),
154                                 "EMAIL",  [],
155                                 "LICENSE",  "GPL",
156                                 "MODE", "async",
157                                 "EDITINPLACE", false);
158   ## Make sure the state variables survive "clear functions".
159   mlock;
160
161   if (nargin == 2)
162     switch (toupper (file))
163     case "EDITOR"
164       FUNCTION.EDITOR = state;
165     case "HOME"
166       if (! isempty (state) && state(1) == "~")
167         state = [ default_home, state(2:end) ];
168       endif
169       FUNCTION.HOME = state;
170     case "AUTHOR"
171       FUNCTION.AUTHOR = state;
172     case "EMAIL"
173       FUNCTION.EMAIL = state;
174     case "LICENSE"
175       FUNCTION.LICENSE = state;
176     case "MODE"
177       if (strcmp (state, "sync") || strcmp (state, "async"))
178         FUNCTION.MODE = state;
179       else
180         error('edit: expected "edit MODE sync|async"');
181       endif
182     case "EDITINPLACE"
183       if (ischar (state))
184         if (strcmpi (state, "true"))
185           state = true;
186         elseif (strcmpi (state, "false"))
187           state = false;
188         else
189           state = eval (state);
190         endif
191       endif
192       FUNCTION.EDITINPLACE = state;
193     case "GET"
194       if (isfield (FUNCTION, toupper(state)))
195         ret = FUNCTION.(toupper (state));
196       else
197         ret = FUNCTION;
198       endif
199     otherwise
200       error ('edit: expected "edit EDITOR|HOME|AUTHOR|EMAIL|LICENSE|MODE val"');
201     endswitch
202     return
203   endif
204
205   ## Start the editor without a file if no file is given.
206   if (nargin < 1)
207     if (exist (FUNCTION.HOME, "dir") == 7 && (isunix () || ! ispc ()))
208       system (cstrcat ("cd \"", FUNCTION.HOME, "\" ; ",
209                       sprintf (FUNCTION.EDITOR, "")),
210               [], FUNCTION.MODE);
211     else
212       system (sprintf (FUNCTION.EDITOR,""), [], FUNCTION.MODE);
213     endif
214     return;
215   endif
216
217   ## Check whether the user is trying to edit a builtin of compiled function.
218   switch (exist (file))
219     case {3, 5}
220       error ("edit: unable to edit a built-in or compiled function");
221   endswitch
222
223   ## Checks for whether the file is
224   ## absolute or relative should be handled inside file_in_loadpath.
225   ## That way, it will be possible to look up files correctly given
226   ## partial path information.  For example, you should be able to
227   ## edit a particular overloaded function by doing any one of
228   ##
229   ##   edit classname/foo
230   ##   edit classname/foo.m
231   ##   edit @classname/foo
232   ##   edit @classname/foo.m
233   ##
234   ## This functionality is needed for other functions as well (at least
235   ## help and type; there may be more).  So the place to fix that is in
236   ## file_in_loadpath, possibly with some help from the load_path class.
237
238   ## The code below includes a portion that serves as a place-holder for
239   ## the changes suggested above.
240
241   ## Create list of explicit and implicit file names.
242   filelist = {file};
243   ## If file has no extension, add file.m and file.cc to the list.
244   idx = rindex (file, ".");
245   if (idx == 0)
246     ## Create the list of files to look for
247     filelist = {file};
248     if (isempty (regexp (file, '\.m$')))
249       ## No ".m" at the end of the file, add to the list.
250       filelist{end+1} = cat (2, file, ".m");
251     endif
252     if (isempty (regexp (file, '\.cc$')))
253       ## No ".cc" at the end of the file, add to the list.
254       filelist{end+1} = cat (2, file, ".cc");
255     endif
256   endif
257
258   ## If the file includes a path, it may be an overloaded function.
259   if (! strcmp (file, "@") && index (file, filesep))
260     ## No "@" at the beginning of the file, add to the list.
261     numfiles = numel(filelist);
262     for n = 1:numfiles
263       filelist{n+numfiles} = cat (2, "@", filelist{n});
264     endfor
265   endif
266
267   ## Search the entire path for the 1st instance of a file in the list.
268   fileandpath = "";
269   for n = 1:numel(filelist)
270     filetoedit = file_in_path (path, filelist{n});
271     if (! isempty (filetoedit))
272       ## The path is explicitly included.
273       fileandpath = filetoedit;
274       break;
275     endif
276   endfor
277
278   if (! isempty (fileandpath))
279     ## If the file exists, then edit it.
280     if (FUNCTION.EDITINPLACE)
281       ## Edit in place even if it is protected.
282       system (sprintf (FUNCTION.EDITOR, cstrcat ("\"", fileandpath, "\"")),
283               [], FUNCTION.MODE);
284       return;
285     else
286       ## If the file is modifiable in place then edit it, otherwise make
287       ## a copy in HOME and then edit it.
288       fid = fopen (fileandpath, "r+t");
289       if (fid < 0)
290         from = fileandpath;
291         fileandpath = cstrcat (FUNCTION.HOME, from (rindex (from, filesep):end));
292         [status, msg] = copyfile (from, fileandpath, 1);
293         if (status == 0)
294           error (msg);
295         endif
296       else
297         fclose (fid);
298       endif
299       system (sprintf (FUNCTION.EDITOR, cstrcat ("\"", fileandpath, "\"")),
300               [], FUNCTION.MODE);
301       return;
302     endif
303   endif
304
305   ## If editing a new file that is neither a m-file or an oct-file,
306   ## just edit it.
307   fileandpath = file;
308   idx = rindex (file, ".");
309   name = file(1:idx-1);
310   ext = file(idx+1:end);
311   switch (ext)
312     case {"cc", "m"}
313       0;
314     otherwise
315       system (sprintf (FUNCTION.EDITOR, cstrcat ("\"", fileandpath, "\"")),
316               [], FUNCTION.MODE);
317       return;
318   endswitch
319
320   ## The file doesn't exist in path so create it, put in the function
321   ## template and edit it.
322
323   ## Guess the email name if it was not given.
324   if (isempty (FUNCTION.EMAIL))
325     host = getenv("HOSTNAME");
326     if (isempty (host) && ispc ())
327       host = getenv ("COMPUTERNAME");
328     endif
329     if (isempty (host))
330       [status, host] = system ("uname -n");
331       ## trim newline from end of hostname
332       if (! isempty (host))
333         host = host(1:end-1);
334       endif
335     endif
336     if (isempty (host))
337       FUNCTION.EMAIL = " ";
338     else
339       FUNCTION.EMAIL = cstrcat ("<", default_user(0), "@", host, ">");
340     endif
341   endif
342
343   ## Fill in the revision string.
344   now = localtime (time);
345   revs = cstrcat ("Created: ", strftime ("%Y-%m-%d", now));
346
347   ## Fill in the copyright string.
348   copyright = cstrcat (strftime ("Copyright (C) %Y ", now), FUNCTION.AUTHOR);
349
350   ## Fill in the author tag field.
351   author = cstrcat ("Author: ", FUNCTION.AUTHOR, " ", FUNCTION.EMAIL);
352
353   ## Fill in the header.
354   uclicense = toupper (FUNCTION.LICENSE);
355   switch (uclicense)
356     case "GPL"
357       head = cstrcat (copyright, "\n\n", "\
358 This program is free software; you can redistribute it and/or modify\n\
359 it under the terms of the GNU General Public License as published by\n\
360 the Free Software Foundation; either version 3 of the License, or\n\
361 (at your option) any later version.\n\
362 \n\
363 This program is distributed in the hope that it will be useful,\n\
364 but WITHOUT ANY WARRANTY; without even the implied warranty of\n\
365 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n\
366 GNU General Public License for more details.\n\
367 \n\
368 You should have received a copy of the GNU General Public License\n\
369 along with Octave; see the file COPYING.  If not, see\n\
370 <http://www.gnu.org/licenses/>.\
371 ");
372       tail = cstrcat (author, "\n", revs);
373
374     case "BSD"
375       head = cstrcat (copyright, "\n\n", "\
376 This program is free software; redistribution and use in source and\n\
377 binary forms, with or without modification, are permitted provided that\n\
378 the following conditions are met:\n\
379 \n\
380    1.Redistributions of source code must retain the above copyright\n\
381      notice, this list of conditions and the following disclaimer.\n\
382    2.Redistributions in binary form must reproduce the above copyright\n\
383      notice, this list of conditions and the following disclaimer in the\n\
384      documentation and/or other materials provided with the distribution.\n\
385 \n\
386 THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND\n\
387 ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE\n\
388 IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE\n\
389 ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE\n\
390 FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL\n\
391 DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS\n\
392 OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)\n\
393 HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT\n\
394 LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY\n\
395 OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF\n\
396 SUCH DAMAGE.\
397 ");
398       tail = cstrcat (author, "\n", revs);
399
400     case "PD"
401       head = "";
402       tail = cstrcat (author, "\n", revs, "\n\n",
403                      "This program is granted to the public domain.");
404
405     otherwise
406       head = "";
407       tail = cstrcat (copyright, "\n\n", FUNCTION.LICENSE, "\n",
408                      author, "\n", revs);
409   endswitch
410
411   ## Generate the function template.
412   exists = exist (name);
413   switch (ext)
414     case {"cc", "C", "cpp"}
415       if (isempty (head))
416         comment = cstrcat ("/*\n", tail, "\n\n*/\n\n");
417       else
418         comment = cstrcat ("/*\n", head, "\n\n", tail, "\n\n*/\n\n");
419       endif
420       ## If we are shadowing an m-file, paste the code for the m-file.
421       if (any (exists == [2, 103]))
422         code = cstrcat ("\\ ", strrep (type (name), "\n", "\n// "));
423       else
424         code = " ";
425       endif
426       body = cstrcat ("#include <octave/oct.h>\n\n",
427                      "DEFUN_DLD(", name, ",args,nargout,\"\\\n",
428                      name, "\\n\\\n\")\n{\n",
429                      "  octave_value_list retval;\n",
430                      "  int nargin = args.length();\n\n",
431                      code, "\n  return retval;\n}\n");
432
433       text = cstrcat (comment, body);
434     case "m"
435       ## If we are editing a function defined on the fly, paste the
436       ## code.
437       if (any (exists == [2, 103]))
438         body = type (name);
439       else
440         body = cstrcat ("function [ ret ] = ", name, " ()\n\nendfunction\n");
441       endif
442       if (isempty (head))
443         comment = cstrcat ("## ", name, "\n\n",
444                           "## ", strrep (tail, "\n", "\n## "), "\n\n");
445       else
446         comment = cstrcat ("## ", strrep(head,"\n","\n## "), "\n\n", ...
447                           "## ", name, "\n\n", ...
448                           "## ", strrep (tail, "\n", "\n## "), "\n\n");
449       endif
450       text = cstrcat (comment, body);
451   endswitch
452
453   ## Write the initial file (if there is anything to write)
454   fid = fopen (fileandpath, "wt");
455   if (fid < 0)
456     error ("edit: could not create %s", fileandpath);
457   endif
458   fputs (fid, text);
459   fclose (fid);
460
461   ## Finally we are ready to edit it!
462   system (sprintf (FUNCTION.EDITOR, cstrcat ("\"", fileandpath, "\"")),
463           [], FUNCTION.MODE);
464
465 endfunction
466
467 function ret = default_home ()
468
469   ret = getenv ("HOME");
470   if (isempty (ret))
471     ret = glob ("~");
472     if (! isempty (ret))
473       ret = ret{1};
474     else
475       ret = "";
476     endif
477   endif
478
479 endfunction
480
481 ## Return the name associated with the current user ID.
482 ##
483 ## If LONG_FORM is 1, return the full name.  This will be the
484 ## default author.  Otherwise return the login name.
485 ## login@host will be the default email address.
486
487 function ret = default_user (long_form)
488
489   ent = getpwuid (getuid);
490   if (! isstruct (ent))
491     ret = getenv ("USER");
492     if (isempty (ret))
493       ret = getenv ("USERNAME");
494     endif
495   elseif (long_form)
496     ret = ent.gecos;
497     pos = strfind (ret, ",");
498     if (! isempty (pos))
499       ret = ret(1:pos-1);
500     endif
501   else
502     ret = ent.name;
503   endif
504
505 endfunction
506
507 %!test
508 %! s.editor = edit ("get", "editor");
509 %! s.home = edit ("get", "home");
510 %! s.author = edit ("get", "author");
511 %! s.email = edit ("get", "email");
512 %! s.license = edit ("get", "license");
513 %! s.editinplace = edit ("get", "editinplace");
514 %! s.mode = edit ("get", "mode");
515 %! edit editor none
516 %! edit home none
517 %! edit author none
518 %! edit email none
519 %! edit license none
520 %! edit ("editinplace", !s.editinplace)
521 %! if (s.mode(1) == "a")
522 %!   edit mode sync
523 %! else
524 %!   edit mode async
525 %! endif
526 %! edit ("editor", s.editor);
527 %! edit ("home", s.home);
528 %! edit ("author", s.author);
529 %! edit ("email", s.email);
530 %! edit ("license", s.license);
531 %! edit ("editinplace", s.editinplace);
532 %! edit ("mode", s.mode);
533 %! assert (edit ("get", "editor"), s.editor);
534 %! assert (edit ("get", "home"), s.home);
535 %! assert (edit ("get", "author"), s.author);
536 %! assert (edit ("get", "email"), s.email);
537 %! assert (edit ("get", "license"), s.license);
538 %! assert (edit ("get", "editinplace"), s.editinplace);
539 %! assert (edit ("get", "mode"), s.mode);
540