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