Added a crude function detector in the section regex for C code.
[mp-5.x.git] / mp_file.mpsl
blob99f64db2af5aa48fc1dfd89d23346ba4f7ba6b8a
1 /*
3     Minimum Profit 5.x
4     A Programmer's Text Editor
6     File manipulation.
8     Copyright (C) 1991-2008 Angel Ortega <angel@triptico.com>
10     This program is free software; you can redistribute it and/or
11     modify it under the terms of the GNU General Public License
12     as published by the Free Software Foundation; either version 2
13     of the License, or (at your option) any later version.
15     This program is distributed in the hope that it will be useful,
16     but 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.
20     You should have received a copy of the GNU General Public License
21     along with this program; if not, write to the Free Software
22     Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
24     http://www.triptico.com
28 /** editor actions **/
30 mp.actions['new']       = sub (d) {
31         d = mp.find_file_by_name(L("<unnamed>"));
33         if (d != -1) {
34                 mp.active_i = d;
35                 d = mp.active();
36         }
37         else
38                 d = mp.new();
41 mp.actions['next']      = sub (d) { mp.next(); };
42 mp.actions['prev']      = sub (d) { mp.prev(); };
44 mp.actions['save_as']   = sub (d) {
46         local t;
48         if ((t = mp.savefile(L("Save file as:"))) == NULL)
49                 return;
51         /* store new name */
52         d.name = t;
54         if (mp.long_op(mp.save, d) == -1)
55                 mp.alert(sprintf(L("Error saving file: %s"), ERRNO));
56         else
57                 mp.detect_syntax(d);
60 mp.actions['save']      = sub (d) {
62         /* name is <unnamed> or something similar; ask for one */
63         if (regex("/^<.+>$/", d.name))
64                 mp.actions.save_as(d);
65         else
66         if (mp.long_op(mp.save, d) == -1)
67                 mp.alert(sprintf(L("Error saving file: %s"), ERRNO));
70 mp.actions['close']     = sub (d) {
72         if (d.txt.mod) {
73                 local r;
74                 r = mp.confirm(L("File has changed. Save changes?"));
76                 /* cancel? don't close */
77                 if (r == 0)
78                         return;
79                 if (r == 1)
80                         mp.actions.save(d);
81         }
83         mp.close();
86 mp.actions['exit']      = sub (d) {
88         local s;
90         if (mp.config.auto_sessions)
91                 mp.save_session();
93         if (mp.actions.close_all())
94                 mp.exit();
97 mp.actions['open']      = sub (d) {
99         local n;
101         if ((n = mp.openfile(L("File to open:"))) != NULL && n ne "")
102                 if (mp.long_op(mp.open, n) == NULL && ERRNO != NULL)
103                         mp.alert(sprintf("Error opening '%s': %s", n, ERRNO));
106 mp.actions['revert']    = sub (d) {
107         /* save current name */
108         local p = d.name;
110         if (d.txt.mod) {
111                 local r;
112                 r = mp.confirm(L("File has changed. Are you sure?"));
114                 /* cancel? don't close */
115                 if (r == 0 || r == 2)
116                         return;
117         }
119         mp.close();
120         if (mp.long_op(mp.open, p) == NULL && ERRNO != NULL)
121                 mp.alert(sprintf("Error opening '%s': %s", p, ERRNO));
124 mp.actions['open_config_file']  = sub (d) {
126         mp.open(HOMEDIR ~ ".mp.mpsl");
129 mp.actions['sync'] = sub (d) {
131         /* save all modified documents */
132         foreach (d, grep(sub (e) { e.txt.mod; }, mp.docs))
133                 mp.actions.save(d);
136 mp.actions['exec_command']      = sub (d) {
138         local t = mp.form( [
139                 { 'label' => L("System command:"),
140                   'type' => 'text',
141                   'history' => 'system' }
142                 ]);
144         if (t != NULL) {
145                 local cmd = t[0];
147                 /* does it start with a pipe? */
148                 if (regex('/^\|/', cmd)) {
149                         local p;
151                         /* yes; current document should be fed to it */
152                         cmd = sregex('/^\|/', cmd, NULL);
154                         if ((p = popen(cmd, "w")) != NULL) {
155                                 foreach (l, mp.get_active_area(d))
156                                         write(p, l ~ mp.config.eol);
158                                 pclose(p);
159                         }
160                         else
161                                 mp.drv.alert(
162                                         sprintf(L("Error writing to command '%s'"), cmd));
163                 }
164                 else {
165                         /* no; execute command and insert into cursor */
166                         local p;
168                         if ((p = popen(cmd, "r")) != NULL) {
169                                 local l;
171                                 mp.store_undo(d);
173                                 while ((l = read(p)) != NULL)
174                                         mp.insert(d, l);
176                                 pclose(p);
177                         }
178                         else
179                                 mp.drv.alert(
180                                         sprintf(L("Error reading from command '%s'"), cmd));
181                 }
182         }
185 mp.actions['close_all'] = sub {
187         local s;
189         while (s = size(mp.docs)) {
190                 local doc = mp.docs[mp.active_i];
191                 
192                 /* close current document */
193                 mp.actions.close(doc);
195                 /* if the size of the list hasn't changed,
196                    action was cancelled, so don't exit */
197                 if (s == size(mp.docs))
198                         return 0;
199         }
201         return 1;
204 mp.actions['open_under_cursor'] = sub (d) {
205         local w;
207         /* is the word under cursor file:line: ? */
208         if ((w = mp.get_word(d, '/[a-z\.\_0-9\/-]+:[0-9]+: ?/i')) != NULL) {
209                 w = split(':', w);
211                 /* open the file */
212                 local n = mp.open(w[0]);
214                 /* now move to the line */
215                 mp.set_y(n, w[1] - 1);
216         }
217         else
218         if ((w = mp.get_word(d, '/[a-z\.\_0-9\/-]+/i')) != NULL) {
219                 mp.open(w);
220         }
223 /** default key bindings **/
225 mp.keycodes['ctrl-n']           = 'next';
226 mp.keycodes['ctrl-o']           = 'open';
227 mp.keycodes['ctrl-q']           = 'exit';
228 mp.keycodes['ctrl-s']           = 'save';
229 mp.keycodes['ctrl-w']           = 'close';
230 mp.keycodes['ctrl-enter']       = 'open_under_cursor';
232 mp.keycodes['close-window']     = 'exit';
234 /** action descriptions **/
236 mp.actdesc['new']       = LL("New");
237 mp.actdesc['save']      = LL("Save...");
238 mp.actdesc['save_as']   = LL("Save as...");
239 mp.actdesc['next']      = LL("Next");
240 mp.actdesc['prev']      = LL("Previous");
241 mp.actdesc['open']      = LL("Open...");
242 mp.actdesc['exit']      = LL("Exit");
243 mp.actdesc['close']     = LL("Close");
244 mp.actdesc['revert']    = LL("Revert");
245 mp.actdesc['close_all'] = LL("Close all");
247 mp.actdesc['open_config_file']          = LL("Edit configuration file");
248 mp.actdesc['open_templates_file']       = LL("Edit templates file");
249 mp.actdesc['sync']                      = LL("Save modified texts");
250 mp.actdesc['exec_command']              = LL("Run system command...");
251 mp.actdesc['open_under_cursor']         = LL("Open file under cursor");
253 /** code **/
255 sub mp.chomp(str)
256 /* chomps the end of file chars from a string */
258         sregex("/\r?\n$/", str, NULL);
262 sub mp.save(doc)
263 /* saves a file */
265         local f;
266         local s = NULL;
267         local nl = 0;
269         /* if unlink before write is desired, do it */
270         if (mp.config.unlink && (s = stat(doc.name)) != NULL)
271                 unlink(doc.name);
273         if ((f = open(doc.name, "wb")) == NULL) {
274                 /* can't write? delete name */
275                 doc.name = L("<unnamed>");
276                 return -1;
277         }
279         /* if the document has a password, save it encrypted */
280         if (doc.password)
281                 nl = mp.crypt1_save(f, doc.txt.lines, doc.password);
282         else {
283                 /* save as a plain text file */
284                 foreach (l, doc.txt.lines) {
285                         /* write a line separator if it's not the first line */
286                         if (nl)
287                                 write(f, mp.config.eol);
289                         write(f, l);
290                         nl++;
291                 }
292         }
294         close(f);
296         doc.txt.mod = 0;
298         /* set back the permissions and ownership, if available */
299         if (s != NULL) {
300                 chmod(doc.name, s[2]);
301                 chown(doc.name, s[4], s[5]);
302         }
304         return nl;
308 sub mp.new(filename, lines)
309 /* creates a new document */
311         local doc, txt;
313         txt = {};
314         txt.x = 0;
315         txt.y = 0;
316         txt.vx = 0;
317         txt.vy = 0;
318         txt.lines = lines || [ '' ];
319         txt.mod = 0;
321         doc = {};
322         doc.name = filename || L("<unnamed>");
323         doc.txt = txt;
325         doc.undo = [];
326         doc.redo = [];
328         doc.syntax = NULL;
330         /* store in the list and set as active */
331         push(mp.docs, doc);
332         mp.active_i = size(mp.docs) - 1;
334         /* autodetect syntax */
335         mp.detect_syntax(doc);
337         return doc;
341 sub mp.next()
342 /* rotates through the document list */
344         if (++mp.active_i == size(mp.docs))
345                 mp.active_i = 0;
349 sub mp.prev()
350 /* rotates through the document list, backwards */
352         if (--mp.active_i == -1)
353                 mp.active_i = size(mp.docs) - 1;
357 sub mp.close()
358 /* closes the active document */
360         local k = mp.active_i;
362         /* delete from the list */
363         adel(mp.docs, mp.active_i);
365         /* rotate if it was the last one */
366         if (mp.active_i == size(mp.docs))
367                 mp.active_i = 0;
371 sub mp.find_file_by_name(filename)
372 /* finds an open file by its name */
374         seek(map(sub(d) { d.name; }, mp.docs), filename);
378 sub mp.open(filename)
379 /* opens a new document (uses UI) */
381         local s;
383         /* looks first if the file is already open */
384         if ((s = mp.find_file_by_name(filename)) != -1) {
385                 mp.active_i = s;
386                 return mp.active();
387         }
389         if ((s = stat(filename)) == NULL) {
390                 mp.message = {
391                         'timeout' => time() + 2,
392                         'string'  => sprintf(L("New file '%s'"), filename)
393                 };
395                 return mp.new(filename);
396         }
398         /* canonicalize, if possible */
399         if (s[13] != NULL) {
400                 filename = s[13];
402                 /* look again for this filename in the open files */
403                 if ((s = mp.find_file_by_name(filename)) != -1) {
404                         mp.active_i = s;
405                         return mp.active();
406                 }
407         }
409         local d, f;
411         if ((f = open(filename, "rb")) == NULL)
412                 return NULL;
413         else {
414                 if (mp.crypt1_detect(f)) {
415                         /* password needed; ask for it */
416                         local p;
418                         if ((p = mp.form( [
419                                 { 'label'       => L("Password:"),
420                                   'type'        => 'password' }
421                                 ])) == NULL) {
422                                 /* cancel? fail, but not on error */
423                                 return NULL;
424                         }
426                         /* get the password */
427                         p = p[0];
429                         /* an empty password is equal to cancellation */
430                         if (p eq '')
431                                 return NULL;
433                         /* and load the file */
434                         d = mp.new(filename, mp.crypt1_load(f, p));
435                         d.password = p;
436                 }
437                 else {
438                         /* close file (needed for rewinding AND
439                            possible encoding autodetection) */
440                         close(f);
442                         /* reopen and read */
443                         f = open(filename, "rb");
444                         d = mp.new(filename, mp.plain_load(f));
445                 }
447                 close(f);
448         }
450         return d;