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