blob: f20d01f984e0a32b4c6518b32ab49215a4a6e7b9 [file] [log] [blame]
MG Mud User88f12472016-06-24 23:31:02 +02001// MorgenGrauen MUDlib
2//
3// fileview.c
4//
5// $Id: fileview.c 9142 2015-02-04 22:17:29Z Zesstra $
Zesstraefad7be2018-11-06 23:18:30 +01006#pragma strict_types, rtt_checks
Zesstra0d64cca2020-03-09 21:03:56 +01007#pragma range_check
MG Mud User88f12472016-06-24 23:31:02 +02008#pragma no_clone
MG Mud User88f12472016-06-24 23:31:02 +02009
10#include <ansi.h>
11#include <player/base.h>
12#include <wizlevels.h>
13#include <shells.h>
14#include <daemon/mand.h>
15#include <udp.h>
Zesstraefad7be2018-11-06 23:18:30 +010016#include <files.h>
Zesstrac70bf582019-11-26 00:43:17 +010017#include <rtlimits.h>
Zesstraefad7be2018-11-06 23:18:30 +010018
MG Mud User88f12472016-06-24 23:31:02 +020019#define NEED_PROTOTYPES
20#include <magier.h>
21#include <thing/properties.h>
22#include <player.h>
23
Zesstraa2bb5bf2021-04-20 17:45:22 +020024struct lsformats {
25 mapping templates;
26 mapping colormap;
27};
28private nosave struct lsformats lstemplates;
MG Mud User88f12472016-06-24 23:31:02 +020029private nosave mapping oldman_result;
30
31// ###################
32//######################### INITIALISIERUNG #############################
33// ###################
34
35mixed _query_localcmds()
36{
37 return ({({"ls","_ls",0,LEARNER_LVL}),
38 ({"more","_more",0,LEARNER_LVL}),
39 ({"cat","_cat",0,LEARNER_LVL}),
40 ({"head","_cat",0,LEARNER_LVL}),
41 ({"tail","_cat",0,LEARNER_LVL}),
42 ({"man","_man",0,LEARNER_LVL}),
43 ({"rman","_rman",0,LEARNER_LVL}),
44 ({"showprops","_showprops",0,WIZARD_LVL}),
45 ({"grep","_grep",0,LEARNER_LVL})});
46}
47
48
49// ######
50//################################ LS ###################################
51// ######
52
53//
54// _ls: Der ls-Befehl: Verzeichnisse und Dateien anzeigen
55// cmdline: Kommandozeile
56//
57
Zesstraa2bb5bf2021-04-20 17:45:22 +020058// Helfer fuer map() zum extrahieren benutzerdefinierte Farben aus P_VARIABLES
MG Mud User88f12472016-06-24 23:31:02 +020059private string _get_color(string color)
60{
61 return COLORS[lower_case(color)];
62}
63
Zesstraa2bb5bf2021-04-20 17:45:22 +020064// Ermittelt Farbtags und Formattemplates mit Platzhaltern fuer ls-Ausgaben
65// und bereitet beides fuer die Nutzung durch terminal_colours() vor.
MG Mud User88f12472016-06-24 23:31:02 +020066private void SetColorstrings()
67{
Zesstraa2bb5bf2021-04-20 17:45:22 +020068 mapping vars = QueryProp(P_VARIABLES);
69 // 2nd value in templates is the additional amount of space the templates
70 // itself needs in addition to the value inserted later in place of %s.
71 lstemplates = (<lsformats> templates: m_allocate(3,2),
72 colormap:m_allocate(4,1) );
73 // 0-Key ist der default, wenn ein Platzhalter nicht gefunden wird. Wird
74 // z.B. fuer Ende-Tags benutzt.
75 lstemplates.colormap[0]=ANSI_NORMAL;
76 // Template fuer die Ausgabe und die Defaults fuer die Farbplatzhalter je
77 // nach Terminal einstellen.
Arathornb3051452021-05-13 21:13:03 +020078 switch(QueryProp(P_TTY))
MG Mud User88f12472016-06-24 23:31:02 +020079 {
80 case "vt100":
Zesstraa2bb5bf2021-04-20 17:45:22 +020081 lstemplates.templates[DIR] = "%^LS_DIR_START%^%s/%^LS_DIR_END%^";
82 lstemplates.templates[DIR, 1] = 1;
83 lstemplates.templates[OBJ] = "%^LS_OBJ_START%^%s%^LS_OBJ_END%^";
84 lstemplates.templates[VC] = "%^LS_VC_START%^%s%^LS_VC_END%^";
85 lstemplates.colormap["LS_DIR_START"]=ANSI_BOLD;
86 lstemplates.colormap["LS_OBJ_START"]=ANSI_INVERS;
87 lstemplates.colormap["LS_VC_START"]=ANSI_INVERS;
88 // Die *_END Platzhalter sind nicht explizit aufgefuehrt, solange sie
89 // nur ANSI_NORMAL normal waeren - in dem Fall benutzt terminal_colour()
90 // spaeter den 0-Key in colormap
91 break;
MG Mud User88f12472016-06-24 23:31:02 +020092 case "ansi":
Zesstraa2bb5bf2021-04-20 17:45:22 +020093 lstemplates.templates[DIR] = "%^LS_DIR_START%^%s%^LS_DIR_END%^";
94 lstemplates.templates[OBJ] = "%^LS_OBJ_START%^%s%^LS_OBJ_END%^";
95 lstemplates.templates[VC] = "%^LS_VC_START%^%s%^LS_VC_END%^";
96 lstemplates.colormap["LS_DIR_START"]=ANSI_BLUE+ANSI_BOLD;
97 lstemplates.colormap["LS_OBJ_START"]=ANSI_RED;
98 lstemplates.colormap["LS_VC_START"]=ANSI_PURPLE;
99 // Die *_END Platzhalter sind nicht explizit aufgefuehrt, solange sie
100 // nur ANSI_NORMAL normal waeren - in dem Fall benutzt terminal_colour()
101 // spaeter den 0-Key in colormap
MG Mud User88f12472016-06-24 23:31:02 +0200102 break;
Zesstraa2bb5bf2021-04-20 17:45:22 +0200103 // In diesen Faellen keine Farbcodeplatzhalter, nur zeichenbasierte
104 // Markierungen anhaengen.
MG Mud User88f12472016-06-24 23:31:02 +0200105 case "dumb":
MG Mud User88f12472016-06-24 23:31:02 +0200106 default:
Zesstraa2bb5bf2021-04-20 17:45:22 +0200107 m_add(lstemplates.templates, DIR, "%s/", 1);
108 m_add(lstemplates.templates, OBJ, "%s*", 1);
109 m_add(lstemplates.templates, VC, "%s*", 1);
110 break;
MG Mud User88f12472016-06-24 23:31:02 +0200111 }
Zesstraa2bb5bf2021-04-20 17:45:22 +0200112 // Die Defaults von oben koennen mit magierindividuellen Einstellungen
113 // ueberschrieben werden. Offensichtlich sind die nur wirksam fuer
114 // vt100/ansi und man koennte auch ANSI-Sequenzen einstellen, die ein vt100
115 // nicht versteht...
116 if(stringp(vars["LS_DIR"]))
117 lstemplates.colormap["LS_DIR_START"]=implode(map(
118 explode(vars["LS_DIR"],"+"), #'_get_color),"");
119 if(stringp(vars["LS_OBJ"]))
120 lstemplates.colormap["LS_OBJ_START"]=implode(map(
121 explode(vars["LS_OBJ"],"+"), #'_get_color),"");
122 if(stringp(vars["LS_VC"]))
123 lstemplates.colormap["LS_VC_START"]=implode(map(
124 explode(vars["LS_VC"],"+"), #'_get_color),"");
MG Mud User88f12472016-06-24 23:31:02 +0200125}
126
127
Bugfix97ce1d92025-04-08 18:51:23 +0200128private string _ls_output_short(<int|string>* filedata, int maxlen,
129 int counter, int maxcount)
MG Mud User88f12472016-06-24 23:31:02 +0200130{
Bugfix97ce1d92025-04-08 18:51:23 +0200131 string filename = filedata[BASENAME];
132 maxlen -= sizeof(filename);
MG Mud User88f12472016-06-24 23:31:02 +0200133 switch(filedata[FILESIZE])
134 {
Bugfix97ce1d92025-04-08 18:51:23 +0200135 case FSIZE_DIR:
136 filename = sprintf(lstemplates.templates[DIR], filename);
137 maxlen -= lstemplates.templates[DIR, 1];
138 break;
139 case FSIZE_NOFILE:
140 filename = sprintf(lstemplates.templates[VC], filename);
141 maxlen -= lstemplates.templates[VC, 1];
142 break;
143 default:
144 if(find_object(filedata[FULLNAME]))
145 {
146 maxlen -= lstemplates.templates[OBJ, 1];
147 filename = sprintf(lstemplates.templates[OBJ], filename);
148 break;
149 }
MG Mud User88f12472016-06-24 23:31:02 +0200150 }
Bugfix97ce1d92025-04-08 18:51:23 +0200151 if(!maxcount)
152 return terminal_colour(filename + "\n", lstemplates.colormap);
153 return terminal_colour(
154 sprintf(
155 "%-*s%s",
156 (maxlen + sizeof(filename)),
157 filename,
158 ((counter++) == maxcount ? (counter = 0, "\n") : " ")),
159 lstemplates.colormap);
MG Mud User88f12472016-06-24 23:31:02 +0200160}
161
Bugfix97ce1d92025-04-08 18:51:23 +0200162private int _ls_maxlen(<int|string>* filedata,int flags, int maxlen)
MG Mud User88f12472016-06-24 23:31:02 +0200163{
Bugfix97ce1d92025-04-08 18:51:23 +0200164 string base = filedata[BASENAME];
165 if(!(flags & LS_A) && base[0] == '.') return 0;
166 maxlen = max(maxlen, sizeof(base));
MG Mud User88f12472016-06-24 23:31:02 +0200167 return 1;
168}
169
170private string _ls_output_long(mixed filedata, int flags,closure valid_read,
171 closure valid_write,closure creator_file)
172{
Zesstraefad7be2018-11-06 23:18:30 +0100173 string base=filedata[BASENAME];
174 if (!(sizeof(base)) || ( (!(flags&LS_A)) && (base[0]=='.')) )
MG Mud User88f12472016-06-24 23:31:02 +0200175 return 0;
Zesstraefad7be2018-11-06 23:18:30 +0100176 int size=filedata[FILESIZE];
177 string path=filedata[PATHNAME];
Zesstraf22b6a82018-11-07 22:39:55 +0100178 string full=filedata[FULLNAME];
Zesstraefad7be2018-11-06 23:18:30 +0100179 int dir=(size==FSIZE_DIR);
180 object ob=find_object(full);
181 int ftime=filedata[FILEDATE];
182 string date;
Bugfix59cb0e52019-09-06 15:17:29 +0200183 if ((time()-ftime)>31536000) // ein Jahr
Zesstraa2bb5bf2021-04-20 17:45:22 +0200184 date=strftime("%b %e %Y", ftime);
Zesstraefad7be2018-11-06 23:18:30 +0100185 else
186 date=strftime("%b %e %H:%M", ftime);
187
188 string creator="";
189 string group="";
MG Mud User88f12472016-06-24 23:31:02 +0200190 if (flags&LS_U)
191 {
Vanion50652322020-03-10 21:13:25 +0100192 creator=({string})call_other(master(),"creator_file", full);
MG Mud User88f12472016-06-24 23:31:02 +0200193 switch(creator)
194 {
195 case ROOTID: creator="root"; break;
Zesstraefad7be2018-11-06 23:18:30 +0100196 case BACKBONEID: creator="std"; break;
197 case MAILID: creator="mail"; break;
198 case NEWSID: creator="news"; break;
199 case NOBODY: creator="nobody"; break;
200 case POLIZEIID: creator="polizei"; break;
201 case DOCID: creator="doc"; break;
202 case GUILDID: creator="gilde"; break;
203 case ITEMID: creator="items"; break;
MG Mud User88f12472016-06-24 23:31:02 +0200204 default: if(!creator) creator="none"; break;
205 }
206 }
207 if (flags&LS_G)
208 {
Zesstraf22b6a82018-11-07 22:39:55 +0100209 string *tmp=explode(path, "/") - ({""});
MG Mud User88f12472016-06-24 23:31:02 +0200210 if (sizeof(tmp))
211 {
212 switch(tmp[0])
213 {
214 case WIZARDDIR: group="magier"; break;
Zesstraefad7be2018-11-06 23:18:30 +0100215 case NEWSDIR: group="news"; break;
216 case MAILDIR: group="mail"; break;
217 case FTPDIR: group="public"; break;
218 case PROJECTDIR: group="project"; break;
MG Mud User88f12472016-06-24 23:31:02 +0200219 case DOMAINDIR: if (sizeof(tmp)>1) { group=tmp[1]; break; }
220 default: group="mud"; break;
221 }
222 }
223 else group="mud";
224 }
Zesstraa2bb5bf2021-04-20 17:45:22 +0200225 if (dir) base=sprintf(lstemplates.templates[DIR],base);
MG Mud User88f12472016-06-24 23:31:02 +0200226 else
227 {
228 if (ob)
229 {
Zesstraefad7be2018-11-06 23:18:30 +0100230 if (size==FSIZE_NOFILE)
Zesstraa2bb5bf2021-04-20 17:45:22 +0200231 base=sprintf(lstemplates.templates[VC],base);
MG Mud User88f12472016-06-24 23:31:02 +0200232 else
Zesstraa2bb5bf2021-04-20 17:45:22 +0200233 base=sprintf(lstemplates.templates[OBJ],base);
MG Mud User88f12472016-06-24 23:31:02 +0200234 }
235 }
Zesstraa2bb5bf2021-04-20 17:45:22 +0200236 return terminal_colour(
237 sprintf(("%c%c%c%c %3d" + ((flags&LS_U) ? " %-24.24s" : "%-0.1s")
Zesstraefad7be2018-11-06 23:18:30 +0100238 +((flags&LS_G) ? " %-8.8s" : "%0.1s") + " %8s %s %s\n"),
239 (dir ? 'd' : '-'),
MG Mud User88f12472016-06-24 23:31:02 +0200240 (!funcall(valid_read,full,getuid(),
Zesstraefad7be2018-11-06 23:18:30 +0100241 "read_file",this_object()) ? '-' : 'r'),
MG Mud User88f12472016-06-24 23:31:02 +0200242 (!funcall(valid_write,full,getuid(),
Zesstraefad7be2018-11-06 23:18:30 +0100243 "write_file",this_object()) ? '-' : 'w'),
244 (ob ? 'x' : '-'),
245 (dir ? (sizeof((get_dir(full+"/*")||({}))-({".",".."}))) : 0),
246 creator, group,
247 (dir ? "-" : size==FSIZE_NOFILE ? "<vc>" : to_string(size)),
Zesstraa2bb5bf2021-04-20 17:45:22 +0200248 date, base),
249 lstemplates.colormap);
MG Mud User88f12472016-06-24 23:31:02 +0200250}
251
Bugfix97ce1d92025-04-08 18:51:23 +0200252private string* _ls_list(< <int|string>* >* targets, int flags, closure v_read,
253 closure v_write, closure creator)
254{
255 string* entries;
256 if (flags&LS_L)
257 {
258 entries = map(targets, #'_ls_output_long, flags, v_read,
259 v_write, creator) - ({0});
260 }
261 else
262 {
263 int counter, maxlen;
264 targets = filter(targets, #'_ls_maxlen, flags, &maxlen);
265 if (maxlen > 76) maxlen = 76;
266 // maxcount muss immer um 1 niedriger sein als die tatsaechliche Anzahl an
267 // moeglichen Spalten, weil gegen maxcount++ und nicht gegen ++maxcount
268 // geprueft wird.
Bugfix2cd4f712025-04-08 21:28:12 +0200269 int maxcount;
270 if(!(flags & LS_1))
271 {
272 maxcount = (78 / (maxlen + 2)) - 1;
273 }
Bugfix97ce1d92025-04-08 18:51:23 +0200274 entries = map(targets, #'_ls_output_short, maxlen, &counter, maxcount);
275 }
276 return entries;
277}
278
MG Mud User88f12472016-06-24 23:31:02 +0200279
280static int _ls(string cmdline)
281{
Bugfix97ce1d92025-04-08 18:51:23 +0200282 int flags;
MG Mud User88f12472016-06-24 23:31:02 +0200283
Bugfix97ce1d92025-04-08 18:51:23 +0200284 string* args = parseargs(_unparsed_args() || "", &flags, LS_OPTS, 1);
285 if(flags == -1)
MG Mud User88f12472016-06-24 23:31:02 +0200286 return USAGE("ls [-" LS_OPTS "] [<Verzeichnis>] [<Verzeichnis2> ..]");
287 SetColorstrings();
Bugfix97ce1d92025-04-08 18:51:23 +0200288 if(!sizeof(args))
289 args = ({ QueryProp(P_CURRENTDIR) });
290 < <int|string>* >* targets = file_list(args,MODE_LSA,0,"/");
291 if(!sizeof(targets))
292 {
293 notify_fail("ls: Keine passenden Verzeichnisse gefunden.\n");
294 return 0;
295 }
Arathorn06b52922018-11-26 20:47:10 +0100296
Bugfix97ce1d92025-04-08 18:51:23 +0200297 // Files sortieren die erste
298 int cmp;
299 if(flags & LS_T)
300 cmp = FILEDATE;
301 else if(flags & LS_S)
302 cmp = FILESIZE;
303 else
304 cmp = BASENAME;
305
306 closure sort_fun;
307 // Dateinamen aufsteigend sortieren, Groesse und Aenderungsdatum absteigend.
308 if(cmp == BASENAME && !(flags & LS_R) || cmp != BASENAME && (flags & LS_R))
309 {
310 sort_fun = function int (<int|string>* a, <int|string>* b) {
Arathorn06b52922018-11-26 20:47:10 +0100311 return (a[cmp] > b[cmp]);
312 };
Bugfix97ce1d92025-04-08 18:51:23 +0200313 }
Arathorn06b52922018-11-26 20:47:10 +0100314 else
Bugfix97ce1d92025-04-08 18:51:23 +0200315 {
316 sort_fun = function int (<int|string>* a, <int|string>* b) {
Arathorn06b52922018-11-26 20:47:10 +0100317 return (a[cmp] < b[cmp]);
318 };
Bugfix97ce1d92025-04-08 18:51:23 +0200319 }
320 targets = sort_array(targets, sort_fun);
321
322 // Ausgabeformat bestimmen
323 closure v_read,v_write,creator;
MG Mud User88f12472016-06-24 23:31:02 +0200324 if (flags&LS_L)
325 {
326 v_read=VALID_READ_CL;
327 v_write=VALID_WRITE_CL;
328 creator=CREATOR_CL;
329 }
Bugfix97ce1d92025-04-08 18:51:23 +0200330 string output = "";
331 // Falls mehr als ein Ziel angegeben wurde, werden zuerst die Ziele
332 // aufgelistet. Anschließend wird in der Schleife der Inhalt von allen Zielen,
333 // die Verzeichnise sind aufgelistet.
334 // Wurde nur ein Ziel angegeben und dies ist eine Datei, wird diese angezeigt.
335 // In der Schleife wird nichts hinzugefügt, weil hier nur Verzeichnisse
336 // aufgelistet werden.
337 if(sizeof(targets) > 1 || targets[0][FILESIZE] >= 0)
MG Mud User88f12472016-06-24 23:31:02 +0200338 {
Bugfix97ce1d92025-04-08 18:51:23 +0200339 string* entries = _ls_list(targets, flags, v_read, v_write, creator);
340 output = sprintf(
341 "\n%d Dateien/Unterverzeichnisse:\n%s\n",
342 sizeof(entries),
343 implode(entries, ""));
344 }
345 foreach(<int|string>* target : targets)
346 {
347 if(target[FILESIZE] != FSIZE_DIR)
348 continue;
349
350 < <int|string>* >* sub_targets = file_list(({target[FULLNAME] + "/*"}),
351 MODE_LSB, 0, "/");
352 sub_targets = sort_array(sub_targets, sort_fun);
353 string* entries = _ls_list(sub_targets, flags, v_read, v_write, creator);
354 if(sizeof(entries))
355 {
356 output += sprintf(
357 "\n%s: Verzeichnis, %d Dateien/Unterverzeichnisse:\n%s\n",
358 target[FULLNAME],
359 sizeof(entries),
360 implode(entries, ""));
361 }
MG Mud User88f12472016-06-24 23:31:02 +0200362 else
363 {
Bugfix97ce1d92025-04-08 18:51:23 +0200364 output += sprintf("\n%s: Leeres Verzeichnis.\n", target[FULLNAME]);
MG Mud User88f12472016-06-24 23:31:02 +0200365 }
366 }
367 More(output);
368 return 1;
369}
370
371// ########
372//############################### MORE ###################################
373// ########
374//
375// _more_file: Mehrere Files hintereinander ausgeben
376//
377
378private void _more_file(string *arg)
379{
380 if (!sizeof(arg)) return;
381 printf("more: Naechste Datei: %s\n",arg[0]);
Zesstra3de3a032018-11-08 19:17:03 +0100382 More(arg[0],1,#'_more_file,
383 (sizeof(arg)>1 ? ({ arg[1..]}) : ({})) );
MG Mud User88f12472016-06-24 23:31:02 +0200384 return;
385}
386
387
388private mixed _size_filter(mixed *arg)
389{
390 if (arg[FILESIZE]>0) return arg[FULLNAME];
391 if (arg[FILESIZE]==0)
392 {
393 printf("%s: %s: Leere Datei.\n",query_verb()||"more",arg[FULLNAME]);
394 return 0;
395 }
Zesstraefad7be2018-11-06 23:18:30 +0100396 if (arg[FILESIZE]==FSIZE_DIR)
MG Mud User88f12472016-06-24 23:31:02 +0200397 printf("%s: %s ist ein Verzeichnis.\n",query_verb()||"more",arg[FULLNAME]);
398 else
399 printf("%s: %s: Datei existiert nicht.\n", query_verb()||"more",
400 arg[FULLNAME]);
401 return 0;
402}
403
404
405//
406// _more: Dateien anzeigen
407// cmdline: Kommandozeile
408//
409
410static int _more(string cmdline)
411{
412 mixed *args;
413 int flags;
414 cmdline=_unparsed_args();
415 args=parseargs(cmdline,&flags,"",1);
416 if (flags==-1||!sizeof(args)) return USAGE("more <datei> [<datei2>..]");
417 args=file_list(args,MODE_MORE,0,"/");
418 if (!sizeof(args))
419 return printf("more: %s: Keine passende Datei gefunden.\n",cmdline),1;
420 args=map(args,#'_size_filter)-({0});
421 if (sizeof(args)) _more_file(args);
422 return 1;
423}
424
425// ###################
426//########################## HEAD, TAIL, CAT #############################
427// ###################
428
429static int _cat(string cmdline)
430{
431 mixed *args;
432 int flags;
433 cmdline=_unparsed_args();
434 args=parseargs(cmdline,&flags,"",1);
435 if(flags==-1||!sizeof(args))
436 return USAGE(query_verb()+" <dateiname> [<datei2>..]");
437 args=file_list(args,MODE_CAT,0,"/");
438 if (!sizeof(args))
439 return printf("%s: %s: Keine passende Datei gefunden.\n",query_verb(),
440 cmdline),1;
441 args=map(args,#'_size_filter)-({0});
442 if (!sizeof(args)) return 1;
443 while(sizeof(args))
444 {
445 switch(query_verb())
446 {
447 case "cat":
448 if (!cat(args[0]))
449 printf("cat: %s konnte nicht angezeigt werden.\n",args[0]);
450 break;
451 case "head":
452 if (!cat(args[0],0,10))
453 printf("head: %s konnte nicht angezeigt werden.\n",args[0]);
454 break;
455 case "tail": tail(args[0]); break;
456 }
Zesstra3de3a032018-11-08 19:17:03 +0100457 if (sizeof(args) > 1)
458 args=args[1..];
459 else
460 break;
MG Mud User88f12472016-06-24 23:31:02 +0200461 }
462 return 1;
463}
464
465// #######
466//############################### MAN ###################################
467// #######
468
Arathorn99c40582020-08-28 19:46:23 +0200469/* Sortiert ein Array paarweise, wobei von jedem Wertepaar der erste Wert
470 fuer die Sortierung herangezogen wird. */
471private string* SortInPairs(string* arr) {
472 if (!sizeof(arr))
473 return arr;
474
475 string** helper = ({});
476
477 /* In Sub-Arrays zerlegen, die jeweils ein Wertepaart enthalten:
478 ({s0, s1, s2, s3}) => ({({s0,s1}), ({s2,s3})}) */
479 int listsize = sizeof(arr);
480 for(int i ; i<listsize ; i+=2) {
481 helper += ({ arr[i..i+1] });
482 }
483
484 // Nach dem ersten Element jedes Sub-Arrays sortieren.
485 helper = sort_array(helper, function int (string* h1, string* h2) {
486 return lower_case(h1[0]) > lower_case(h2[0]);
487 });
488
489 // Eingabe-Array loeschen und aus den sortierten Wertepaaren neu aufbauen.
490 arr = ({});
491 foreach(string* h : helper) {
492 arr += h;
493 }
494
495 return arr;
496}
497
MG Mud User88f12472016-06-24 23:31:02 +0200498static int _man(string cmdline)
499{
MG Mud User88f12472016-06-24 23:31:02 +0200500 int i, flags;
Arathorndb201cc2020-08-28 17:02:38 +0200501 string *input;
MG Mud User88f12472016-06-24 23:31:02 +0200502
Arathorn1facd5f2020-08-28 16:41:17 +0200503 string* args = parseargs(_unparsed_args(), &flags, MAN_OPTS, 0);
504
MG Mud User88f12472016-06-24 23:31:02 +0200505 if (flags==-1 ||
506 (sizeof(args)!=1 &&
507 (sizeof(args)<2 || sizeof(args[1])>1 || !(i=to_int(args[1])))))
508 return USAGE("man [-" MAN_OPTS "] <hilfeseite>");
MG Mud User88f12472016-06-24 23:31:02 +0200509
Arathorn72c7e542020-08-28 16:50:28 +0200510 input = explode(args[0], "/");
511
Arathorn815f2742020-08-28 17:29:04 +0200512 /* Wenn das Ergebnis einer vorherigen Suche noch vorliegt und die aktuelle
513 Eingabe als einziges eine einstellige Zahl enthaelt, und diese dann in
514 dem alten Suchergebnis enthalten ist, wird die Eingabe durch das alte
515 Ergebnis ersetzt. <i> wird in dem Fall geloescht.
516 Wenn kein altes Ergebnis gefunden wurde, enthaelt <i> die eingegebene
517 Nummer. */
Arathorn72c7e542020-08-28 16:50:28 +0200518 if (oldman_result && sizeof(input)==1 && sizeof(args)==1 &&
519 sizeof(input[0])==1 && (i=to_int(input[0])) &&
Arathornd0a1e662020-08-28 17:01:24 +0200520 member(oldman_result,i))
521 {
Arathorn72c7e542020-08-28 16:50:28 +0200522 input = ({oldman_result[i,0], oldman_result[i,1]});
Arathornd0a1e662020-08-28 17:01:24 +0200523 i = 0;
MG Mud User88f12472016-06-24 23:31:02 +0200524 }
Arathorn815f2742020-08-28 17:29:04 +0200525 /* Ansonsten wenn weder -m, noch -r gesetzt sind und es eine Manpage gibt,
526 die genau der Eingabe entspricht, wird diese verwendet. */
Arathorn72c7e542020-08-28 16:50:28 +0200527 else if (!(flags&(MAN_M|MAN_R)) && sizeof(input)>1)
MG Mud User88f12472016-06-24 23:31:02 +0200528 {
Arathornd0a1e662020-08-28 17:01:24 +0200529 if (file_size(MAND_DOCDIR+args[0]) >= 0)
Arathorn72c7e542020-08-28 16:50:28 +0200530 input = ({input[<1], args[0]});
MG Mud User88f12472016-06-24 23:31:02 +0200531 else
Arathorn72c7e542020-08-28 16:50:28 +0200532 input = ({});
MG Mud User88f12472016-06-24 23:31:02 +0200533 }
534 else
535 {
Arathorn815f2742020-08-28 17:29:04 +0200536 /* Soll eine Regexp-Suche durchgefuehrt werden? Dann erstmal den Ausdruck
537 auf Gueltigkeit pruefen. */
MG Mud User88f12472016-06-24 23:31:02 +0200538 if (flags&MAN_R)
539 {
Arathornd0a1e662020-08-28 17:01:24 +0200540 flags &= (~MAN_M);
541 if (catch(regexp(({""}), args[0])) || !regexp(({""}), args[0]))
542 {
543 printf("man: Ungueltiger Ausdruck in Maske.\n");
544 return 1;
545 }
MG Mud User88f12472016-06-24 23:31:02 +0200546 }
Arathorn815f2742020-08-28 17:29:04 +0200547 /* Die Ausgabe von locate() liefert ein String-Array, das abwechselnd den
548 Dateinamen der gefundenen Manpage und den vollen Pfad dieser Manpage
549 unterhalb von /doc enthaelt. Beispielsweise ({"Defend","lfun/Defend"})
550 */
Arathorn72c7e542020-08-28 16:50:28 +0200551 input = ({string *})MAND->locate(args[0], flags&(MAN_M|MAN_R));
Arathorn84dd5f22020-08-28 17:24:44 +0200552 // Sortierung case-insensitive, ggf. vorhandene Pfade dabei ignorieren
553 // Wird fuer die spaetere Ausgabe der Liste benoetigt.
Arathorn99c40582020-08-28 19:46:23 +0200554 input = SortInPairs(input);
MG Mud User88f12472016-06-24 23:31:02 +0200555 }
556
Arathorn815f2742020-08-28 17:29:04 +0200557 /* Alte Such-Treffer werden entsorgt. */
Arathornd0a1e662020-08-28 17:01:24 +0200558 oldman_result = 0;
559
Arathorn815f2742020-08-28 17:29:04 +0200560 /* <i> kann maximal eine einstellige Zahl sein, 1-9, wenn eine solche als
561 einziges Argument eingegeben wurde und kein passendes Ergebnis in einer
562 vorigen Suchanfrage gefunden wurde.
563
564 <input> kann nur genau dann mehr als 2 Elemente haben, wenn das Ergebnis
565 des Aufrufs MAND->locate() drinsteht. Und nur dann muss ueberhaupt ein
566 hoeheres Wertepaar rausgeschnitten werden, denn wenn <input> bis zu 2
567 Elemente hat, kann damit direkt weitergearbeitet werden.
568
569 Beispiel: bei einer Eingabe von "4" wird hier aus <input> das 7. und
570 8. Element entnommen (Indizes [6..7]). */
Arathorn131bb742020-08-28 17:05:02 +0200571 if(i && sizeof(input)>2 && sizeof(input) >= i*2)
572 input = input[(i*2-2)..(i*2-1)];
Arathornd0a1e662020-08-28 17:01:24 +0200573
Arathorn72c7e542020-08-28 16:50:28 +0200574 switch (sizeof(input))
MG Mud User88f12472016-06-24 23:31:02 +0200575 {
Arathorn815f2742020-08-28 17:29:04 +0200576 /* <input> leer, nichts brauchbares gefunden. */
MG Mud User88f12472016-06-24 23:31:02 +0200577 case 0:
Arathornd0a1e662020-08-28 17:01:24 +0200578 printf("Keine Hilfeseite gefunden fuer '%s'.\n", args[0]);
MG Mud User88f12472016-06-24 23:31:02 +0200579 break;
Arathornd0a1e662020-08-28 17:01:24 +0200580
Arathorn815f2742020-08-28 17:29:04 +0200581 /* Genau 2 Elemente enthalten? Dann kann das direkt ausgegeben werden. */
MG Mud User88f12472016-06-24 23:31:02 +0200582 case 2:
583 /*
584 if (flags&MAN_I)
585 {
586 // BRANCH TO MANUALD
587 return 1;
588 }
589 */
Arathorn72c7e542020-08-28 16:50:28 +0200590 printf("Folgende Hilfeseite wurde gefunden: %s\n", input[1]);
591 More(MAND_DOCDIR+input[1], 1);
MG Mud User88f12472016-06-24 23:31:02 +0200592 return 1;
Arathorn815f2742020-08-28 17:29:04 +0200593
594 /* Alles andere: */
MG Mud User88f12472016-06-24 23:31:02 +0200595 default:
Arathorn131bb742020-08-28 17:05:02 +0200596 i = sizeof(input)/2;
Arathorndb201cc2020-08-28 17:02:38 +0200597 string* output = allocate(i);
Arathorn815f2742020-08-28 17:29:04 +0200598
599 // Inhalt: ([ int nummer : string manpage; string manpage_pfad ])
Arathornd0a1e662020-08-28 17:01:24 +0200600 oldman_result = m_allocate(i, 2);
Arathorn815f2742020-08-28 17:29:04 +0200601
Arathornd0a1e662020-08-28 17:01:24 +0200602 while (i)
MG Mud User88f12472016-06-24 23:31:02 +0200603 {
Arathorn131bb742020-08-28 17:05:02 +0200604 output[i-1] = input[i*2-2];
Arathorn1719b252020-08-28 19:44:32 +0200605 m_add(oldman_result, i, input[i*2-2], input[i*2-1]);
MG Mud User88f12472016-06-24 23:31:02 +0200606 i--;
607 }
Arathorndb5d16d2020-05-11 01:23:18 +0200608
Arathorndb5d16d2020-05-11 01:23:18 +0200609 // Numerierung ergaenzen
Arathorndb201cc2020-08-28 17:02:38 +0200610 foreach(int j : sizeof(output))
Arathornd0a1e662020-08-28 17:01:24 +0200611 {
Arathorndb201cc2020-08-28 17:02:38 +0200612 output[j] = sprintf("%d: %s", j+1, output[j]);
Arathorndb5d16d2020-05-11 01:23:18 +0200613 }
614
615 int tty_cols = QueryProp(P_TTY_COLS)-2;
616 string list = "Es wurden die folgenden, potentiell passenden Seiten "
617 "gefunden:\n";
618
619 // Wer keine Grafik sehen will, bekommt eine andere Anzeige.
Arathornd0a1e662020-08-28 17:01:24 +0200620 if (QueryProp(P_NO_ASCII_ART))
621 {
Arathorndb5d16d2020-05-11 01:23:18 +0200622 // @ als geschuetztes Leerzeichen verwenden, um einen Umbruch
623 // nach den Nummern zu verhindern.
Arathorndb201cc2020-08-28 17:02:38 +0200624 output = map(output, #'regreplace, ": ", ":@", 1);
625 list += break_string(implode(output, " "), tty_cols);
Arathorndb5d16d2020-05-11 01:23:18 +0200626 list = regreplace(list, ":@", ": ", 1);
627 }
Arathornd0a1e662020-08-28 17:01:24 +0200628 else
629 {
Arathorndb5d16d2020-05-11 01:23:18 +0200630 // Anzahl Spalten ausrechnen: Terminalbreite / Laenge des laengsten
Arathorndb201cc2020-08-28 17:02:38 +0200631 // Elements in <output>. Kann der Spaltenmodus von sprintf() an sich
Arathorndb5d16d2020-05-11 01:23:18 +0200632 // selbst, das liefert aber nicht immer so guenstige Ergebnisse.
Arathorndb201cc2020-08-28 17:02:38 +0200633 int maxwidth = max(map(output, #'sizeof));
Arathorndb5d16d2020-05-11 01:23:18 +0200634 int tablecols = tty_cols/maxwidth;
635 list += "-"*tty_cols+"\n"+
Arathorndb201cc2020-08-28 17:02:38 +0200636 sprintf("%#-*.*s\n", tty_cols, tablecols, implode(output,"\n"))+
Arathorndb5d16d2020-05-11 01:23:18 +0200637 "-"*tty_cols+"\n";
638 }
639 printf(list);
MG Mud User88f12472016-06-24 23:31:02 +0200640 break;
641 }
642 return 1;
643}
644
645// ########
646//############################### RMAN ##################################
647// ########
648
649static int _rman(string str)
650{
651 int flags;
652 string *args;
653
654 str=_unparsed_args();
655 args=parseargs(str,&flags,"",0);
656 if (flags==-1||sizeof(args)!=2)
657 return USAGE("rman <hilfeseite> <mudname>");
658 write("man: " +
Vanion50652322020-03-10 21:13:25 +0100659 ({string})call_other(UDP_CMD_DIR+"man","send_request",args[1],args[0]));
MG Mud User88f12472016-06-24 23:31:02 +0200660 return 1;
661}
662
663
664// #############
665//############################# SHOWPROPS ###############################
666// #############
667
668//
669// _showprops: Zeigt Properties zu einem Objekt an
670//
671
672static int _showprops(string str)
673{
674 int i;
675 string *list, ausgabe;
676
677 if (str) {
678 if (str[0]!='/') str="/"+str;
679 if (str[0..4]!="/std/")
680 {
681 printf("showprops: Es koennen nur Properties von Objekten "
682 "aus /std/ angezeigt werden.\n");
683 return 1;
684 }
685 if (str[<1]=='.') str+="c";
686 if (str[<2..<1]!=".c") str+=".c";
687 if (file_size(str)<0)
688 {
689 printf("showprops: %s: Es gibt kein Objekt diesen Namens.\n",str[0..<3]);
690 return 1;
691 }
Zesstraefad7be2018-11-06 23:18:30 +0100692 if (catch(load_object(str)))
MG Mud User88f12472016-06-24 23:31:02 +0200693 {
694 printf("showprops: %s: Datei konnte nicht geladen werden.\n",str);
695 return 1;
696 }
697 }
698 if (!str || !find_object(str)) {
699 notify_fail("Welche Properties moechtest Du sehen?"
700 " Bsp.: <showprops /std/npc>\n");
701 return 0;
702 }
703 list=inherit_list(find_object(str));
MG Mud User88f12472016-06-24 23:31:02 +0200704 list=map(list,(: return $1[5..<2]+"h"; :));
705 list+=map(list,(: return explode($1,"/")[<1]; :));
706 list=map(m_indices(mkmapping(list)),(: return "/sys/"+$1; :));
707 list=filter(list,(: return file_size($1)>0; :));
MG Mud User88f12472016-06-24 23:31:02 +0200708 list=sort_array(list, #'<);
709 ausgabe="";
710 for (i=sizeof(list);i--;)
711 {
MG Mud User88f12472016-06-24 23:31:02 +0200712 str=implode(filter(explode(read_file(list[i]),"\n"),
713 (: return $1[0..9]=="#define P_";:)),"\n");
MG Mud User88f12472016-06-24 23:31:02 +0200714 if (str!="") ausgabe+=sprintf("%s\n%s\n\n", list[i], str);
715 }
716 if (ausgabe!="")
717 More(ausgabe);
718 else
719 printf("Keine Property-Definitionen zu diesem Objekt gefunden!\n");
720 return 1;
721}
722
723// ########
724//############################### GREP ###################################
725// ########
726
MG Mud User88f12472016-06-24 23:31:02 +0200727//
728// grep_file: Datei greppen
729// rexpr: Regular Expression
730// flags: Flags
731//
732
733private int grep_file(mixed filedata, string rexpr, int flags)
734{
Zesstrac70bf582019-11-26 00:43:17 +0100735 string fullname=filedata[FULLNAME];
MG Mud User88f12472016-06-24 23:31:02 +0200736 if ((flags&GREP_F)&&fullname=="/players/"+getuid()+"/grep.out")
Zesstraefad7be2018-11-06 23:18:30 +0100737 {
738 write_file("/players/"+getuid()+"/grep.out",
739 "Uebergehe grep.out ...\n");
740 return RET_FAIL;
MG Mud User88f12472016-06-24 23:31:02 +0200741 }
742 switch(filedata[FILESIZE])
743 {
Zesstraefad7be2018-11-06 23:18:30 +0100744 case FSIZE_DIR: return RET_FAIL;
745 case FSIZE_NOFILE: return ERROR(DOESNT_EXIST,fullname,RET_FAIL);
MG Mud User88f12472016-06-24 23:31:02 +0200746 case 0: return RET_FAIL;
747 default: break;
748 }
Zesstraefad7be2018-11-06 23:18:30 +0100749 if (!MAY_READ(fullname))
750 return ERROR(NO_READ,fullname,RET_FAIL);
Zesstrac70bf582019-11-26 00:43:17 +0100751
752 // Bei case-insensitiver Suche das Pattern in Kleinschreibung wandeln
MG Mud User88f12472016-06-24 23:31:02 +0200753 if (flags&GREP_I)
754 rexpr=lower_case(rexpr);
Zesstrac70bf582019-11-26 00:43:17 +0100755
756 // Portionsweise das komplette File einlesen.
757 int maxlen = query_limits()[LIMIT_BYTE];
758 int ptr;
759 bytes read_buffer;
760 bytes data = b"";
761 while (sizeof(read_buffer = read_bytes(fullname, ptr, maxlen)))
MG Mud User88f12472016-06-24 23:31:02 +0200762 {
Zesstrac70bf582019-11-26 00:43:17 +0100763 data += read_buffer;
764 ptr += sizeof(read_buffer);
765 // die Schleifenbedingung erkennt zwar auch, wenn das File vollstaendig
766 // ist, aber wir koennen den Speicherzugriff auch einsparen und schauen,
767 // ob wir schon alles haben.
768 if (ptr >= filedata[FILESIZE])
769 break;
MG Mud User88f12472016-06-24 23:31:02 +0200770 }
Zesstrac70bf582019-11-26 00:43:17 +0100771 // In string konvertieren, wir gehen davon aus, das File ist UTF8-kodiert.
772 string text = to_text(data, "UTF-8");
773 string *lines = explode(text, "\n");
774 int count; // Anzahl Zeilen mit Treffern
775 <string|string*> result = ({}); // zutreffende Zeilen
776 int linecount = 1;
777 foreach(string line: lines)
MG Mud User88f12472016-06-24 23:31:02 +0200778 {
Zesstrac70bf582019-11-26 00:43:17 +0100779 string orig_line = line;
780 // Suche case-insensitive?
781 if (flags&GREP_I)
782 line = lower_case(line);
783 int match = regmatch(line, rexpr) != 0;
784 if (flags&GREP_V) match = !match; // Match ggf. invertieren
MG Mud User88f12472016-06-24 23:31:02 +0200785 if (match)
786 {
Zesstrac70bf582019-11-26 00:43:17 +0100787 // Ausgeben oder nicht?
788 if (!(flags&GREP_C))
MG Mud User88f12472016-06-24 23:31:02 +0200789 {
Zesstrac70bf582019-11-26 00:43:17 +0100790 // Mit Zeilennummer?
791 if (flags&GREP_N)
792 result+=({ sprintf("%4d %s", linecount, orig_line)});
MG Mud User88f12472016-06-24 23:31:02 +0200793 else
Zesstrac70bf582019-11-26 00:43:17 +0100794 result+=({orig_line});
MG Mud User88f12472016-06-24 23:31:02 +0200795 }
Zesstrac70bf582019-11-26 00:43:17 +0100796 ++count;
MG Mud User88f12472016-06-24 23:31:02 +0200797 }
Zesstrac70bf582019-11-26 00:43:17 +0100798 ++linecount;
MG Mud User88f12472016-06-24 23:31:02 +0200799 }
Zesstrac70bf582019-11-26 00:43:17 +0100800
MG Mud User88f12472016-06-24 23:31:02 +0200801 if (count)
802 {
Zesstrac70bf582019-11-26 00:43:17 +0100803 // Bei -h werden die Dateinamen unterdrueckt.
804 if (flags&GREP_H)
805 fullname="";
MG Mud User88f12472016-06-24 23:31:02 +0200806 else
Zesstrac70bf582019-11-26 00:43:17 +0100807 fullname=sprintf("%s ",fullname);
808
809 if (flags&GREP_C)
810 result=sprintf("%s%d passende Zeile%s.\n",fullname, count,
811 (count==1?"":"n"));
812 else
813 result = ( (sizeof(fullname) ? fullname + "\n" : "")
814 + implode(result,"\n") + "\n");
Zesstrac70bf582019-11-26 00:43:17 +0100815
Zesstra77c0bf22020-01-07 22:27:27 +0100816 // Ergebnis ausgeben in File oder an Magier
817 if (flags&GREP_F)
818 return write_file("/players/"+getuid()+"/grep.out",result);
819 write(result);
820 }
MG Mud User88f12472016-06-24 23:31:02 +0200821 return RET_OK;
822}
823
824static int _grep(string cmdline)
825{
826 string rexpr,mask;
827 mixed *args;
828 int flags;
829 cmdline=_unparsed_args();
830 args=parseargs(cmdline,&flags,GREP_OPTS,0);
831 if ((flags==-1)||!sizeof(args))
832 return USAGE("grep [-" GREP_OPTS
833 "] <regexp> <datei/verz> [<datei2> ... ] [<maske>]");
834 rexpr=args[0];
835 if (catch(regexp(({""}),rexpr))||!regexp(({""}),rexpr))
836 return printf("grep: Ungueltiger Suchausdruck: %s\n",rexpr) ,1;
Zesstracc3f2502018-11-14 23:46:47 +0100837 args=(sizeof(args)>1 ? args[1..] : ({}) );
MG Mud User88f12472016-06-24 23:31:02 +0200838 if (flags&GREP_M)
839 {
840 mask=args[<1];
Zesstracc3f2502018-11-14 23:46:47 +0100841 if (sizeof(args) > 2)
842 args = (sizeof(args) > 1 ? args[0..<2] : ({}) );
MG Mud User88f12472016-06-24 23:31:02 +0200843 }
844 if (!sizeof(args))
845 return USAGE("grep [-" GREP_OPTS
846 "] <regexp> <datei/verz> [<datei2> ... ] [<maske>]");
847 args=map(args,#'to_filename)-({0});
MG Mud User88f12472016-06-24 23:31:02 +0200848 args=file_list(args,MODE_GREP,(flags&GREP_R?1:0),"/",mask);
849 if (!sizeof(args))
850 return printf("Keine passenden Dateien gefunden.\n"),1;
851 if (flags&GREP_I) rexpr=lower_case(rexpr);
852 if (flags&GREP_F)
853 {
Zesstraefad7be2018-11-06 23:18:30 +0100854 if (file_size("/players/"+getuid()+"/grep.out")==FSIZE_DIR
855 || !MAY_WRITE("/players/"+getuid()+"/grep.out"))
MG Mud User88f12472016-06-24 23:31:02 +0200856 return printf("grep: Datei /players/%s/grep.out kann nicht "
Zesstraefad7be2018-11-06 23:18:30 +0100857 "geschrieben werden.\n",getuid()),1;
MG Mud User88f12472016-06-24 23:31:02 +0200858 else
859 write_file("/players/"+getuid()+"/grep.out",
860 "Ausgabe von \"grep " + _unparsed_args() + "\":\n");
861 }
862 asynchron(args,#'grep_file,rexpr,flags,0);
863 return 1;
864}