blob: c0eb327b4f293ad18301b1c09a379a241bd0f541 [file] [log] [blame]
Zesstrad1a16b22016-07-18 19:55:51 +02001#pragma strict_types
2
3/* nedit.c
4 Editor fuer news, mail usw.
5 Version 1 (C) 1993 Loco
6 Version 2 (C) 1995 Wargon
7
8 Verwendung ausserhalb von Morgengrauen ist gestattet unter folgenden
9 Bedingungen:
10 - Benutzung erfolgt auf eigene Gefahr. Jegliche Verantwortung wird
11 abgelehnt.
12 - Auch in veraenderten oder abgeleiteten Objekten muss ein Hinweis auf
13 die Herkunft erhalten bleiben.
14 Ein Update-Service besteht nicht.
15
16 Anwendung:
17 #inherit "/mail/nedit"; (zusaetzlich zu anderen inherits)
18
19 nedit(string funcname); oder
20 nedit(string funcname,string pretext);
21 Nach Beendigung des Editors wird die Funktion funcname aufgerufen.
22 Sie bekommt als Argument einen string mit dem fertigen editierten Text
23 uebergeben bzw. 0, wenn der Editor mit ~q abgebrochen wurde.
24 Optionales Argument pretext ist der zu editierende Text. Wenn 0 oder nicht
25 uebergeben, wird ein neuer Text begonnen.
26*/
27
28#include <properties.h>
29#include <wizlevels.h>
30#include <defines.h>
31#include <input_to.h>
32
33#define TP this_player()
34
35#define F_OVR 1 // Overwrite-Modus
36#define F_BLK 2 // Blocksatz
37
38#define MAX_LINES 1000
39
40static mixed nedittext, nexitfunc, editor_used;
41static int bstart, bend; // Anfang und Ende des Blocks
42static int cur; // Aktuelle Zeile
43static int len; // Laenge des Gesamttextes
44static int flags; // Overwritemodus
45
46static int get_edit_line(string str);
47static int ShowWritten(int f, int l, int num);
Zesstrad1a16b22016-07-18 19:55:51 +020048static int ShowHelp();
49static int delLine(int l);
50static void delBlock();
51static int input_func();
52varargs static void moveBlock(int start, int end, int real);
53
54void init_rescue() {
55 add_action("RescueText","~r");
56}
57
58private string nedit_prompt()
59{
60 if (sizeof(nedittext) <= MAX_LINES)
61 return("]");
62 else {
63 nedittext = nedittext[0..MAX_LINES-1];
64 len = MAX_LINES;
65 if (cur >= MAX_LINES)
66 cur = MAX_LINES-1;
67 if (bstart >= MAX_LINES)
68 bstart = MAX_LINES-1;
69 if (bend >= MAX_LINES)
70 bend = MAX_LINES-1;
71 return sprintf("*** Mehr als %d Zeilen! Text wurde abgeschnitten! ***\n]", MAX_LINES);
72 }
73}
74
75static varargs int nedit(string exitfunc,string pretext) {
76 if (editor_used) {
77 write("Offensichtlich schreibt hier schon jemand dran. Sorry.\n"+
78 "(Falls du es selbst bist, tippe ~r zur Wiederaufnahme einer verlorenen Sitzung)\n");
79 return -1;
80 }
81 if (!pretext) nedittext=({""});
82 else nedittext=explode(pretext,"\n");
83 bstart = 0;
84 bend = cur = len = sizeof(nedittext)-1;
85 nexitfunc=exitfunc;
86 flags = 0;
87 editor_used=(string)TP->query_real_name();
88 if (pretext)
89 get_edit_line("~z");
90 else {
91 //nedit_prompt();
92 input_to("get_edit_line", INPUT_PROMPT, nedit_prompt());
93 }
94 return 1;
95}
96
97static int get_edit_line(string str) {
98 int err;
99 int spaces;
100 int fflag;
101 int sl;
102
103 if (!str) str="";
Zesstra08cb9c22019-10-24 23:11:10 +0200104 // Kontrollzeichen rausfiltern
105 str = regreplace(str,"[[:cntrl:]]","",RE_PCRE|RE_GLOBAL);
Zesstrad1a16b22016-07-18 19:55:51 +0200106 fflag = 0;
107
108 sl = sizeof(str);
109
110 if (str=="**" || str=="~." || str==".") {
111 editor_used=0;
112 str = implode(nedittext, "\n");
113 nedittext = 0;
114 call_other(this_object(),nexitfunc,str);
115 return 1;
116 }
117 if (str[0..0]== "~" && sl >= 2) {
118 if (sl == 2) {
119 switch(str[0..1]) {
120 // Abbruch:
121 case "~q":
122 editor_used = 0;
123 return call_other(this_object(), nexitfunc, 0);
124 // Temporaer rausgehen:
125 case "~!":
126 write("Mit ~r kannst Du weiterschreiben.\n");
127 init_rescue();
128 return 1;
129 // Gesamten Text anzeigen
130 case "~R":
131 fflag = 1;
132 case "~r":
133 return ShowWritten(0, len, fflag);
134 // Ausschnitt um Cursor anzeigen
135 case "~Z":
136 fflag = 1;
137 case "~z":
138 int f = (cur > 5) ? cur-5 : 0;
139 int l = (cur < len-5) ? cur + 5 : len;
140 return ShowWritten(f, l, fflag);
141 // Hilfeseite anzeigen:
142 case "~h":
143 return ShowHelp();
144 // Zeile ueber Cursor loeschen:
145 case "~d":
146 return delLine(cur-1);
147 // Block loeschen:
148 case "~D":
149 delBlock();
150 write( "Block geloescht.\n" );
151 return input_func();
152 // Overwrite-Modus toggeln:
153 case "~v":
154 flags ^= F_OVR;
155 printf("%smodus eingeschaltet.\n",
156 (flags & F_OVR) ? "Ueberschreib" : "Einfuege");
157 return input_func();
158 // Blocksatz toggeln:
159 case "~b":
160 flags ^= F_BLK;
161 printf("%ssatz aktiviert.\n",
162 (flags & F_BLK) ? "Block" : "Flatter");
163 return input_func();
164 // Statusinfo anzeigen:
165 case "~s":
166 printf("Zeile: %d | Laenge: %d | BStart: %d | BEnde: %d | "
167 "Modus: %s/%s.\n",
168 cur, len, bstart, bend, (flags&F_OVR)?"Ueber.":"Einfg.",
169 (flags&F_BLK)?"Blocksatz":"Flattersatz");
170 return input_func();
171 // Block verschieben:
172 case "~m":
173 moveBlock(bstart, bend, 1);
174 return input_func();
175 // Block umformatieren:
176 case "~F":
177 int bs = bstart;
178 str = implode(nedittext[bstart..bend], " ");
179 delBlock();
180 bstart = bend = cur = bs;
181 fflag = -1;
182 write("Block wurde umformatiert.\n");
183 break;
184 // Zeile umformatieren:
185 case "~f":
186 str = nedittext[cur];
187 nedittext = nedittext[0..cur-1] + nedittext[cur+1..];
188 if (cur <= bstart)
189 bstart--;
190 if (cur <= bend)
191 bend--;
192 len --;
193 fflag = -1;
194 write("Zeile wurde umformatiert.\n");
195 break;
196 } // switch
197 } // if (sl == 2)
198 else {
199 // Cursorbewegungen an bestimmte Stellen:
200 if (str[0..1] == "~c" && sl == 3) {
201 fflag = 1;
202 switch(str[2]) {
203 case 'u': spaces = -1; break;
204 case 'd': spaces = 1; break;
205 case 't': spaces = -len; break;
206 case 'b': spaces = len; break;
207 case 's': spaces = bstart - cur; break;
208 case 'e': spaces = bend - cur; break;
209 default: spaces = 0;
210 fflag = 0;
211 break;
212 }
213 }
214 // Cursorbewegung mit Zeilennummern:
215 if (spaces || sscanf(str, "~c%d", spaces) == 1) {
216 if (fflag>0 || str[2..2] == "+" || str[2..2] == "-")
217 cur += spaces;
218 else
219 cur = spaces-1;
220
221 if (cur < 0)
222 cur = 0;
223 else if (cur > len)
224 cur = len;
225
226 printf("%s\n",nedittext[cur]);
227 return input_func();
228 }
229 // Blockgrenzen setzen:
230 if ((err = (str[0..2] == "~bs")) || (str[0..2]=="~be")) {
231 string out, p;
232 int pos;
233
234 int valid = -1;
235
236 if (sl == 3)
237 pos = valid = cur;
238 else if (sscanf(str[3..], "%d%s", pos, p) == 2 && p=="")
239 valid = pos--;
240
241 if (valid >= 0) {
242 if (err) {
243 bstart = pos;
244 if (pos > bend)
245 bend = len;
246 out = "Blockanfang";
247 }
248 else {
249 if (pos < bstart)
250 bstart = 0;
251 bend = pos;
252 if (len && bend == len)
253 bend--;
254 out = "Blockende";
255 }
256 printf("%s gesetzt.\n", out);
257 return input_func();
258 }
259 }
260
261 // Ersetzen:
262 if ((sizeof(str) >= 8) && str[0..1] == "~s") {
263 string *s1, *s2;
264 int m;
265
266 m = (str[2] == ' ') ? 3 : 2;
267
268 if (sizeof(s1 = explode(str[m..], str[m..m])) == 4) {
269 s2 = explode(nedittext[cur], s1[1]);
270 if (sizeof(s2) > 1) {
271 s2[1] = s2[0]+s1[2]+s2[1];
272 nedittext[cur] = implode(s2[1..], s1[1]);
273 if (s1[3] == "p")
274 printf("%s\n", nedittext[cur]);
275 else
276 write("OK.\n");
277 //nedit_prompt();
278 }
279 else {
280 printf("\"%s\" nicht gefunden!\n", s1[1]);
281 //nedit_prompt();
282 }
283
284 input_to("get_edit_line", INPUT_PROMPT, nedit_prompt());
285 return 1;
286 }
287 }
288 } // if (sl > 2)
289 } // if (str[0..0] == "~")
290
291 spaces=(sizeof(str) && (str[0]==' ' || str[0]=='\t'));
292 if (spaces) str="$"+str; /* Kleiner hack wegen fuehrenden Leerzeichen */
293 str=break_string(str,78,0,(flags&F_BLK) ? BS_BLOCK|BS_NO_PARINDENT : 0);
294 if (spaces) str=str[1..<1];
295 if ( ((str[0..1]=="~r" && sizeof(str)>2) || str[0..1]=="~i") &&
296 IS_LEARNER(TP) )
297 {
298 str=str[2..<2];
299 if (str[0..0]==" ") str=str[1..<1];
300 if (!str || catch(err=file_size(str=(string)"/secure/master"->_get_path(str,getuid(TP)))) || err<0) {
301 write("File nicht gefunden.\n");
302 //nedit_prompt();
303 input_to("get_edit_line", INPUT_PROMPT, nedit_prompt());
304 return 1;
305 }
306 str=read_file(str);
307 if (!str){
308 write("Zu gross!\n");
309 //nedit_prompt();
310 input_to("get_edit_line", INPUT_PROMPT, nedit_prompt());
311 return 1;
312 }
313 write("Ok.\n");
314 }
315 if (str=="" || !str) str="\n";
316 {
317 string *x;
318 int sx;
319
320 x = explode(str, "\n")[0..<2];
321 sx = sizeof(x);
322
323 if (flags&F_OVR && !fflag) {
324 nedittext = nedittext[0..cur-1] + x + nedittext[cur+1..];
325 sx--;
326 }
327 else
328 nedittext = nedittext[0..cur-1] + x + nedittext[cur..];
329
330 if (cur < bstart)
331 bstart += sx;
332 if (cur <= bend)
333 bend += (sx + fflag);
334 cur += (sx + fflag + ((flags&F_OVR) ? 1:0));
335 len += sx;
336
337 // Kann beim Umformatieren des letzten Abschnitts vorkommen.
338 if (nedittext[len] != "") {
339 nedittext += ({ "" });
340 len++;
341 }
342 }
343 //nedit_prompt();
344 input_to("get_edit_line", INPUT_PROMPT, nedit_prompt());
345 return 1;
346}
347
348static int delLine(int l)
349{
350 string pr;
351 if (l < 0)
352 pr="Da ist nix mehr zum Loeschen!\n]";
353 else {
354 if (bstart >= l)
355 bstart--;
356 if (bend >= l)
357 bend--;
358 if (cur >= l)
359 cur--;
360 len--;
361 nedittext=(nedittext[0..l-1]+nedittext[l+1..]);
362 write("Vorherige Zeile geloescht.\n");
363 pr=nedit_prompt();
364 }
365 input_to("get_edit_line", INPUT_PROMPT, pr);
366 return 1;
367}
368
369static void delBlock()
370{
371 if (cur > bstart) {
372 if (cur <= bend)
373 cur = bstart;
374 else
375 cur -= (bend - bstart + 1);
376 }
377 if (bend == len)
378 nedittext = nedittext[0..bstart-1];
379 else
380 nedittext = nedittext[0..bstart-1] + nedittext[bend+1..];
381
382 bend = len = sizeof(nedittext)-1;
383 bstart = 0;
384 if (cur > len)
385 cur = len;
386}
387
388varargs static void moveBlock(int start, int end, int real)
389{
390 int blen;
391 string *block;
392
393 if (cur >= start && cur <= end)
394 write("Aber der Cursor steht innerhalb des Blocks!\n");
395 else {
396 block = nedittext[start..end];
397 blen = sizeof(block)-1;
398 delBlock();
399 nedittext = nedittext[0..cur-1] + block + nedittext[cur..];
400 if (real) {
401 bstart = cur;
402 bend = cur + blen;
403 }
404 len += (blen+1);
405 write("OK.\n");
406 }
407}
408
Zesstrad1a16b22016-07-18 19:55:51 +0200409mixed RescueText() {
410 if (!nedittext || !editor_used)
411 return notify_fail("Du hast nix angefangen zu schreiben!\n"),0;
412 if (TP->query_real_name()!=editor_used)
413 return notify_fail("Hier schreibt "+capitalize(editor_used)+"!\n"),0;
414 if (query_input_pending(TP))
415 return notify_fail("Du schreibst gerade schon irgendwas. Sorry...\n"),0;
416 return ShowWritten(0, len, 0);
417}
418
419static int ShowWritten(int f, int l, int num) {
420 string s, t, c, p, in;
421 int i;
422
423 if (num) {
424 if (l >= 100) {
425 p = "%3d%s%s";
426 in = " ";
427 }
428 else {
429 p = "%2d%s%s";
430 in = " ";
431 }
432 }
433 else
434 in = "";
435
436 for (t="", i=l-1; i>=f; i--) {
437 if (i == cur)
438 c = ( ((i == bend) || (i == bstart)) ? "#" : "*");
439 else if (i==bstart || i==bend)
440 c = ">";
441 else c = ":";
442
443 if (num)
444 t = (sprintf(p, i+1, c, nedittext[i])[0..75] + "\n" + t);
445 else
446 t = (c + nedittext[i]+"\n" + t);
447 }
448
449 if (l==len) {
450 if (len == cur)
451 c = ( (len == bend) ? "#" : "*");
452 else if (len==bend)
453 c = ">";
454 else c = " ";
455 }
456 else c= " ";
457
458 s="Das hast Du bisher geschrieben:\n"+in+" \
459--------------------------\n\
460"+t+in+c+"\
461--------------------------";
462 this_player()->More(s,0,symbol_function("input_func",this_object()));
463 return 1;
464}
465
466static int ShowHelp() {
467 this_player()->More("\
468--------------------------\n\
469Der Editor versteht folgende Befehle:\n\
470--- Dateikommandos:\n\
471~h diese Hilfsseite\n\
472~r / ~R zeigt an, was Du bisher geschrieben hast\n\
473~z / ~Z zeigt den Textausschnitt um den Cursor herum\n\
474"+(IS_WIZARD(TP) ? "\
475~i filename fuegt eine Datei in den Text ein (auch ~r filename)\n\
476" : "" )+ "\
477~q bricht ab\n\
478** oder . beendet sauber\n\
479!<cmd> fuehrt <cmd> aus, wie wenn Du gerade nicht schreiben wuerdest\n\
480~! verlaesst den Editor voruebergehend\n\
481~s Statusinformationen anzeigen\n\
482~b Wechselt zwischen Flatter- (default) und Blocksatz\n\
483--- zeilenorientierte Kommandos:\n\
484~d loescht die letzte Zeile (Text-, nicht Eingabezeile)\n\
485~v wechselt zwischen Einfuege- (default) und Ueberschreibmodus\n\
486~s !s1!s2! Ersetzt das erste Vorkommnis des Strings s1 durch den String s2\n\
487 in der aktuellen Zeile.\n\
488 Statt durch ! koennen die Strings auch durch beliebige andere\n\
489 Zeichen getrennt werden, die weder in s1 noch in s2 vorkommen.\n\
490~f Formatiert die aktuelle Zeile neu\n\
491--- Cursorkommandos:\n\
492~cu / ~cd Cursor um eine Zeile nach oben/unten bewegen\n\
493~ct / ~cb Cursor an Anfang/Ende des Textes bewegen\n\
494~cs / ~ce Cursor an Blockanfang/Blockende bewegen\n\
495~c<nr> Cursor nach Zeile <nr> bewegen\n\
496~c+<nr> Cursor um <nr> Zeilen nach unten bewegen\n\
497~c-<nr> Cursor um <nr> Zeilen nach oben bewegen\n\
498--- blockorientierte Kommandos:\n\
499~bs/~bs<nr> setzt Blockanfang auf Cursorposition bzw. auf Zeile <nr>\n\
500~be/~be<nr> setzt Blockende auf Cursorposition bzw. auf Zeile <nr>\n\
501~F formatiert den Block neu\n\
502~D loescht den markierten Block\n\
503~m verschiebt den markierten Block an die Cursorposition\n\
504---\n\
505Alles andere gilt als Text. Ueberlange Zeilen werden auf eine maximale\n\
506Laenge von 78 Zeichen umgebrochen.\n\
507Nach ~!, oder wenn man waehrend des Schreibens netztot wird, kann man mit\n\
508 ~r wieder in den Editor einsteigen.\n\
509--------------------------\n\
510", 0, symbol_function("input_func", this_object()));
511 return 1;
512}
513
514static int input_func() {
515 //nedit_prompt();
516 input_to("get_edit_line", INPUT_PROMPT, nedit_prompt());
517 return 1;
518}