blob: 85167dd9d6bb7ac3dfa16c288d08875245776cec [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.
269 int maxcount = (78 / (maxlen + 2)) - 1;
270 entries = map(targets, #'_ls_output_short, maxlen, &counter, maxcount);
271 }
272 return entries;
273}
274
MG Mud User88f12472016-06-24 23:31:02 +0200275
276static int _ls(string cmdline)
277{
Bugfix97ce1d92025-04-08 18:51:23 +0200278 int flags;
MG Mud User88f12472016-06-24 23:31:02 +0200279
Bugfix97ce1d92025-04-08 18:51:23 +0200280 string* args = parseargs(_unparsed_args() || "", &flags, LS_OPTS, 1);
281 if(flags == -1)
MG Mud User88f12472016-06-24 23:31:02 +0200282 return USAGE("ls [-" LS_OPTS "] [<Verzeichnis>] [<Verzeichnis2> ..]");
283 SetColorstrings();
Bugfix97ce1d92025-04-08 18:51:23 +0200284 if(!sizeof(args))
285 args = ({ QueryProp(P_CURRENTDIR) });
286 < <int|string>* >* targets = file_list(args,MODE_LSA,0,"/");
287 if(!sizeof(targets))
288 {
289 notify_fail("ls: Keine passenden Verzeichnisse gefunden.\n");
290 return 0;
291 }
Arathorn06b52922018-11-26 20:47:10 +0100292
Bugfix97ce1d92025-04-08 18:51:23 +0200293 // Files sortieren die erste
294 int cmp;
295 if(flags & LS_T)
296 cmp = FILEDATE;
297 else if(flags & LS_S)
298 cmp = FILESIZE;
299 else
300 cmp = BASENAME;
301
302 closure sort_fun;
303 // Dateinamen aufsteigend sortieren, Groesse und Aenderungsdatum absteigend.
304 if(cmp == BASENAME && !(flags & LS_R) || cmp != BASENAME && (flags & LS_R))
305 {
306 sort_fun = function int (<int|string>* a, <int|string>* b) {
Arathorn06b52922018-11-26 20:47:10 +0100307 return (a[cmp] > b[cmp]);
308 };
Bugfix97ce1d92025-04-08 18:51:23 +0200309 }
Arathorn06b52922018-11-26 20:47:10 +0100310 else
Bugfix97ce1d92025-04-08 18:51:23 +0200311 {
312 sort_fun = function int (<int|string>* a, <int|string>* b) {
Arathorn06b52922018-11-26 20:47:10 +0100313 return (a[cmp] < b[cmp]);
314 };
Bugfix97ce1d92025-04-08 18:51:23 +0200315 }
316 targets = sort_array(targets, sort_fun);
317
318 // Ausgabeformat bestimmen
319 closure v_read,v_write,creator;
MG Mud User88f12472016-06-24 23:31:02 +0200320 if (flags&LS_L)
321 {
322 v_read=VALID_READ_CL;
323 v_write=VALID_WRITE_CL;
324 creator=CREATOR_CL;
325 }
Bugfix97ce1d92025-04-08 18:51:23 +0200326 string output = "";
327 // Falls mehr als ein Ziel angegeben wurde, werden zuerst die Ziele
328 // aufgelistet. Anschließend wird in der Schleife der Inhalt von allen Zielen,
329 // die Verzeichnise sind aufgelistet.
330 // Wurde nur ein Ziel angegeben und dies ist eine Datei, wird diese angezeigt.
331 // In der Schleife wird nichts hinzugefügt, weil hier nur Verzeichnisse
332 // aufgelistet werden.
333 if(sizeof(targets) > 1 || targets[0][FILESIZE] >= 0)
MG Mud User88f12472016-06-24 23:31:02 +0200334 {
Bugfix97ce1d92025-04-08 18:51:23 +0200335 string* entries = _ls_list(targets, flags, v_read, v_write, creator);
336 output = sprintf(
337 "\n%d Dateien/Unterverzeichnisse:\n%s\n",
338 sizeof(entries),
339 implode(entries, ""));
340 }
341 foreach(<int|string>* target : targets)
342 {
343 if(target[FILESIZE] != FSIZE_DIR)
344 continue;
345
346 < <int|string>* >* sub_targets = file_list(({target[FULLNAME] + "/*"}),
347 MODE_LSB, 0, "/");
348 sub_targets = sort_array(sub_targets, sort_fun);
349 string* entries = _ls_list(sub_targets, flags, v_read, v_write, creator);
350 if(sizeof(entries))
351 {
352 output += sprintf(
353 "\n%s: Verzeichnis, %d Dateien/Unterverzeichnisse:\n%s\n",
354 target[FULLNAME],
355 sizeof(entries),
356 implode(entries, ""));
357 }
MG Mud User88f12472016-06-24 23:31:02 +0200358 else
359 {
Bugfix97ce1d92025-04-08 18:51:23 +0200360 output += sprintf("\n%s: Leeres Verzeichnis.\n", target[FULLNAME]);
MG Mud User88f12472016-06-24 23:31:02 +0200361 }
362 }
363 More(output);
364 return 1;
365}
366
367// ########
368//############################### MORE ###################################
369// ########
370//
371// _more_file: Mehrere Files hintereinander ausgeben
372//
373
374private void _more_file(string *arg)
375{
376 if (!sizeof(arg)) return;
377 printf("more: Naechste Datei: %s\n",arg[0]);
Zesstra3de3a032018-11-08 19:17:03 +0100378 More(arg[0],1,#'_more_file,
379 (sizeof(arg)>1 ? ({ arg[1..]}) : ({})) );
MG Mud User88f12472016-06-24 23:31:02 +0200380 return;
381}
382
383
384private mixed _size_filter(mixed *arg)
385{
386 if (arg[FILESIZE]>0) return arg[FULLNAME];
387 if (arg[FILESIZE]==0)
388 {
389 printf("%s: %s: Leere Datei.\n",query_verb()||"more",arg[FULLNAME]);
390 return 0;
391 }
Zesstraefad7be2018-11-06 23:18:30 +0100392 if (arg[FILESIZE]==FSIZE_DIR)
MG Mud User88f12472016-06-24 23:31:02 +0200393 printf("%s: %s ist ein Verzeichnis.\n",query_verb()||"more",arg[FULLNAME]);
394 else
395 printf("%s: %s: Datei existiert nicht.\n", query_verb()||"more",
396 arg[FULLNAME]);
397 return 0;
398}
399
400
401//
402// _more: Dateien anzeigen
403// cmdline: Kommandozeile
404//
405
406static int _more(string cmdline)
407{
408 mixed *args;
409 int flags;
410 cmdline=_unparsed_args();
411 args=parseargs(cmdline,&flags,"",1);
412 if (flags==-1||!sizeof(args)) return USAGE("more <datei> [<datei2>..]");
413 args=file_list(args,MODE_MORE,0,"/");
414 if (!sizeof(args))
415 return printf("more: %s: Keine passende Datei gefunden.\n",cmdline),1;
416 args=map(args,#'_size_filter)-({0});
417 if (sizeof(args)) _more_file(args);
418 return 1;
419}
420
421// ###################
422//########################## HEAD, TAIL, CAT #############################
423// ###################
424
425static int _cat(string cmdline)
426{
427 mixed *args;
428 int flags;
429 cmdline=_unparsed_args();
430 args=parseargs(cmdline,&flags,"",1);
431 if(flags==-1||!sizeof(args))
432 return USAGE(query_verb()+" <dateiname> [<datei2>..]");
433 args=file_list(args,MODE_CAT,0,"/");
434 if (!sizeof(args))
435 return printf("%s: %s: Keine passende Datei gefunden.\n",query_verb(),
436 cmdline),1;
437 args=map(args,#'_size_filter)-({0});
438 if (!sizeof(args)) return 1;
439 while(sizeof(args))
440 {
441 switch(query_verb())
442 {
443 case "cat":
444 if (!cat(args[0]))
445 printf("cat: %s konnte nicht angezeigt werden.\n",args[0]);
446 break;
447 case "head":
448 if (!cat(args[0],0,10))
449 printf("head: %s konnte nicht angezeigt werden.\n",args[0]);
450 break;
451 case "tail": tail(args[0]); break;
452 }
Zesstra3de3a032018-11-08 19:17:03 +0100453 if (sizeof(args) > 1)
454 args=args[1..];
455 else
456 break;
MG Mud User88f12472016-06-24 23:31:02 +0200457 }
458 return 1;
459}
460
461// #######
462//############################### MAN ###################################
463// #######
464
Arathorn99c40582020-08-28 19:46:23 +0200465/* Sortiert ein Array paarweise, wobei von jedem Wertepaar der erste Wert
466 fuer die Sortierung herangezogen wird. */
467private string* SortInPairs(string* arr) {
468 if (!sizeof(arr))
469 return arr;
470
471 string** helper = ({});
472
473 /* In Sub-Arrays zerlegen, die jeweils ein Wertepaart enthalten:
474 ({s0, s1, s2, s3}) => ({({s0,s1}), ({s2,s3})}) */
475 int listsize = sizeof(arr);
476 for(int i ; i<listsize ; i+=2) {
477 helper += ({ arr[i..i+1] });
478 }
479
480 // Nach dem ersten Element jedes Sub-Arrays sortieren.
481 helper = sort_array(helper, function int (string* h1, string* h2) {
482 return lower_case(h1[0]) > lower_case(h2[0]);
483 });
484
485 // Eingabe-Array loeschen und aus den sortierten Wertepaaren neu aufbauen.
486 arr = ({});
487 foreach(string* h : helper) {
488 arr += h;
489 }
490
491 return arr;
492}
493
MG Mud User88f12472016-06-24 23:31:02 +0200494static int _man(string cmdline)
495{
MG Mud User88f12472016-06-24 23:31:02 +0200496 int i, flags;
Arathorndb201cc2020-08-28 17:02:38 +0200497 string *input;
MG Mud User88f12472016-06-24 23:31:02 +0200498
Arathorn1facd5f2020-08-28 16:41:17 +0200499 string* args = parseargs(_unparsed_args(), &flags, MAN_OPTS, 0);
500
MG Mud User88f12472016-06-24 23:31:02 +0200501 if (flags==-1 ||
502 (sizeof(args)!=1 &&
503 (sizeof(args)<2 || sizeof(args[1])>1 || !(i=to_int(args[1])))))
504 return USAGE("man [-" MAN_OPTS "] <hilfeseite>");
MG Mud User88f12472016-06-24 23:31:02 +0200505
Arathorn72c7e542020-08-28 16:50:28 +0200506 input = explode(args[0], "/");
507
Arathorn815f2742020-08-28 17:29:04 +0200508 /* Wenn das Ergebnis einer vorherigen Suche noch vorliegt und die aktuelle
509 Eingabe als einziges eine einstellige Zahl enthaelt, und diese dann in
510 dem alten Suchergebnis enthalten ist, wird die Eingabe durch das alte
511 Ergebnis ersetzt. <i> wird in dem Fall geloescht.
512 Wenn kein altes Ergebnis gefunden wurde, enthaelt <i> die eingegebene
513 Nummer. */
Arathorn72c7e542020-08-28 16:50:28 +0200514 if (oldman_result && sizeof(input)==1 && sizeof(args)==1 &&
515 sizeof(input[0])==1 && (i=to_int(input[0])) &&
Arathornd0a1e662020-08-28 17:01:24 +0200516 member(oldman_result,i))
517 {
Arathorn72c7e542020-08-28 16:50:28 +0200518 input = ({oldman_result[i,0], oldman_result[i,1]});
Arathornd0a1e662020-08-28 17:01:24 +0200519 i = 0;
MG Mud User88f12472016-06-24 23:31:02 +0200520 }
Arathorn815f2742020-08-28 17:29:04 +0200521 /* Ansonsten wenn weder -m, noch -r gesetzt sind und es eine Manpage gibt,
522 die genau der Eingabe entspricht, wird diese verwendet. */
Arathorn72c7e542020-08-28 16:50:28 +0200523 else if (!(flags&(MAN_M|MAN_R)) && sizeof(input)>1)
MG Mud User88f12472016-06-24 23:31:02 +0200524 {
Arathornd0a1e662020-08-28 17:01:24 +0200525 if (file_size(MAND_DOCDIR+args[0]) >= 0)
Arathorn72c7e542020-08-28 16:50:28 +0200526 input = ({input[<1], args[0]});
MG Mud User88f12472016-06-24 23:31:02 +0200527 else
Arathorn72c7e542020-08-28 16:50:28 +0200528 input = ({});
MG Mud User88f12472016-06-24 23:31:02 +0200529 }
530 else
531 {
Arathorn815f2742020-08-28 17:29:04 +0200532 /* Soll eine Regexp-Suche durchgefuehrt werden? Dann erstmal den Ausdruck
533 auf Gueltigkeit pruefen. */
MG Mud User88f12472016-06-24 23:31:02 +0200534 if (flags&MAN_R)
535 {
Arathornd0a1e662020-08-28 17:01:24 +0200536 flags &= (~MAN_M);
537 if (catch(regexp(({""}), args[0])) || !regexp(({""}), args[0]))
538 {
539 printf("man: Ungueltiger Ausdruck in Maske.\n");
540 return 1;
541 }
MG Mud User88f12472016-06-24 23:31:02 +0200542 }
Arathorn815f2742020-08-28 17:29:04 +0200543 /* Die Ausgabe von locate() liefert ein String-Array, das abwechselnd den
544 Dateinamen der gefundenen Manpage und den vollen Pfad dieser Manpage
545 unterhalb von /doc enthaelt. Beispielsweise ({"Defend","lfun/Defend"})
546 */
Arathorn72c7e542020-08-28 16:50:28 +0200547 input = ({string *})MAND->locate(args[0], flags&(MAN_M|MAN_R));
Arathorn84dd5f22020-08-28 17:24:44 +0200548 // Sortierung case-insensitive, ggf. vorhandene Pfade dabei ignorieren
549 // Wird fuer die spaetere Ausgabe der Liste benoetigt.
Arathorn99c40582020-08-28 19:46:23 +0200550 input = SortInPairs(input);
MG Mud User88f12472016-06-24 23:31:02 +0200551 }
552
Arathorn815f2742020-08-28 17:29:04 +0200553 /* Alte Such-Treffer werden entsorgt. */
Arathornd0a1e662020-08-28 17:01:24 +0200554 oldman_result = 0;
555
Arathorn815f2742020-08-28 17:29:04 +0200556 /* <i> kann maximal eine einstellige Zahl sein, 1-9, wenn eine solche als
557 einziges Argument eingegeben wurde und kein passendes Ergebnis in einer
558 vorigen Suchanfrage gefunden wurde.
559
560 <input> kann nur genau dann mehr als 2 Elemente haben, wenn das Ergebnis
561 des Aufrufs MAND->locate() drinsteht. Und nur dann muss ueberhaupt ein
562 hoeheres Wertepaar rausgeschnitten werden, denn wenn <input> bis zu 2
563 Elemente hat, kann damit direkt weitergearbeitet werden.
564
565 Beispiel: bei einer Eingabe von "4" wird hier aus <input> das 7. und
566 8. Element entnommen (Indizes [6..7]). */
Arathorn131bb742020-08-28 17:05:02 +0200567 if(i && sizeof(input)>2 && sizeof(input) >= i*2)
568 input = input[(i*2-2)..(i*2-1)];
Arathornd0a1e662020-08-28 17:01:24 +0200569
Arathorn72c7e542020-08-28 16:50:28 +0200570 switch (sizeof(input))
MG Mud User88f12472016-06-24 23:31:02 +0200571 {
Arathorn815f2742020-08-28 17:29:04 +0200572 /* <input> leer, nichts brauchbares gefunden. */
MG Mud User88f12472016-06-24 23:31:02 +0200573 case 0:
Arathornd0a1e662020-08-28 17:01:24 +0200574 printf("Keine Hilfeseite gefunden fuer '%s'.\n", args[0]);
MG Mud User88f12472016-06-24 23:31:02 +0200575 break;
Arathornd0a1e662020-08-28 17:01:24 +0200576
Arathorn815f2742020-08-28 17:29:04 +0200577 /* Genau 2 Elemente enthalten? Dann kann das direkt ausgegeben werden. */
MG Mud User88f12472016-06-24 23:31:02 +0200578 case 2:
579 /*
580 if (flags&MAN_I)
581 {
582 // BRANCH TO MANUALD
583 return 1;
584 }
585 */
Arathorn72c7e542020-08-28 16:50:28 +0200586 printf("Folgende Hilfeseite wurde gefunden: %s\n", input[1]);
587 More(MAND_DOCDIR+input[1], 1);
MG Mud User88f12472016-06-24 23:31:02 +0200588 return 1;
Arathorn815f2742020-08-28 17:29:04 +0200589
590 /* Alles andere: */
MG Mud User88f12472016-06-24 23:31:02 +0200591 default:
Arathorn131bb742020-08-28 17:05:02 +0200592 i = sizeof(input)/2;
Arathorndb201cc2020-08-28 17:02:38 +0200593 string* output = allocate(i);
Arathorn815f2742020-08-28 17:29:04 +0200594
595 // Inhalt: ([ int nummer : string manpage; string manpage_pfad ])
Arathornd0a1e662020-08-28 17:01:24 +0200596 oldman_result = m_allocate(i, 2);
Arathorn815f2742020-08-28 17:29:04 +0200597
Arathornd0a1e662020-08-28 17:01:24 +0200598 while (i)
MG Mud User88f12472016-06-24 23:31:02 +0200599 {
Arathorn131bb742020-08-28 17:05:02 +0200600 output[i-1] = input[i*2-2];
Arathorn1719b252020-08-28 19:44:32 +0200601 m_add(oldman_result, i, input[i*2-2], input[i*2-1]);
MG Mud User88f12472016-06-24 23:31:02 +0200602 i--;
603 }
Arathorndb5d16d2020-05-11 01:23:18 +0200604
Arathorndb5d16d2020-05-11 01:23:18 +0200605 // Numerierung ergaenzen
Arathorndb201cc2020-08-28 17:02:38 +0200606 foreach(int j : sizeof(output))
Arathornd0a1e662020-08-28 17:01:24 +0200607 {
Arathorndb201cc2020-08-28 17:02:38 +0200608 output[j] = sprintf("%d: %s", j+1, output[j]);
Arathorndb5d16d2020-05-11 01:23:18 +0200609 }
610
611 int tty_cols = QueryProp(P_TTY_COLS)-2;
612 string list = "Es wurden die folgenden, potentiell passenden Seiten "
613 "gefunden:\n";
614
615 // Wer keine Grafik sehen will, bekommt eine andere Anzeige.
Arathornd0a1e662020-08-28 17:01:24 +0200616 if (QueryProp(P_NO_ASCII_ART))
617 {
Arathorndb5d16d2020-05-11 01:23:18 +0200618 // @ als geschuetztes Leerzeichen verwenden, um einen Umbruch
619 // nach den Nummern zu verhindern.
Arathorndb201cc2020-08-28 17:02:38 +0200620 output = map(output, #'regreplace, ": ", ":@", 1);
621 list += break_string(implode(output, " "), tty_cols);
Arathorndb5d16d2020-05-11 01:23:18 +0200622 list = regreplace(list, ":@", ": ", 1);
623 }
Arathornd0a1e662020-08-28 17:01:24 +0200624 else
625 {
Arathorndb5d16d2020-05-11 01:23:18 +0200626 // Anzahl Spalten ausrechnen: Terminalbreite / Laenge des laengsten
Arathorndb201cc2020-08-28 17:02:38 +0200627 // Elements in <output>. Kann der Spaltenmodus von sprintf() an sich
Arathorndb5d16d2020-05-11 01:23:18 +0200628 // selbst, das liefert aber nicht immer so guenstige Ergebnisse.
Arathorndb201cc2020-08-28 17:02:38 +0200629 int maxwidth = max(map(output, #'sizeof));
Arathorndb5d16d2020-05-11 01:23:18 +0200630 int tablecols = tty_cols/maxwidth;
631 list += "-"*tty_cols+"\n"+
Arathorndb201cc2020-08-28 17:02:38 +0200632 sprintf("%#-*.*s\n", tty_cols, tablecols, implode(output,"\n"))+
Arathorndb5d16d2020-05-11 01:23:18 +0200633 "-"*tty_cols+"\n";
634 }
635 printf(list);
MG Mud User88f12472016-06-24 23:31:02 +0200636 break;
637 }
638 return 1;
639}
640
641// ########
642//############################### RMAN ##################################
643// ########
644
645static int _rman(string str)
646{
647 int flags;
648 string *args;
649
650 str=_unparsed_args();
651 args=parseargs(str,&flags,"",0);
652 if (flags==-1||sizeof(args)!=2)
653 return USAGE("rman <hilfeseite> <mudname>");
654 write("man: " +
Vanion50652322020-03-10 21:13:25 +0100655 ({string})call_other(UDP_CMD_DIR+"man","send_request",args[1],args[0]));
MG Mud User88f12472016-06-24 23:31:02 +0200656 return 1;
657}
658
659
660// #############
661//############################# SHOWPROPS ###############################
662// #############
663
664//
665// _showprops: Zeigt Properties zu einem Objekt an
666//
667
668static int _showprops(string str)
669{
670 int i;
671 string *list, ausgabe;
672
673 if (str) {
674 if (str[0]!='/') str="/"+str;
675 if (str[0..4]!="/std/")
676 {
677 printf("showprops: Es koennen nur Properties von Objekten "
678 "aus /std/ angezeigt werden.\n");
679 return 1;
680 }
681 if (str[<1]=='.') str+="c";
682 if (str[<2..<1]!=".c") str+=".c";
683 if (file_size(str)<0)
684 {
685 printf("showprops: %s: Es gibt kein Objekt diesen Namens.\n",str[0..<3]);
686 return 1;
687 }
Zesstraefad7be2018-11-06 23:18:30 +0100688 if (catch(load_object(str)))
MG Mud User88f12472016-06-24 23:31:02 +0200689 {
690 printf("showprops: %s: Datei konnte nicht geladen werden.\n",str);
691 return 1;
692 }
693 }
694 if (!str || !find_object(str)) {
695 notify_fail("Welche Properties moechtest Du sehen?"
696 " Bsp.: <showprops /std/npc>\n");
697 return 0;
698 }
699 list=inherit_list(find_object(str));
MG Mud User88f12472016-06-24 23:31:02 +0200700 list=map(list,(: return $1[5..<2]+"h"; :));
701 list+=map(list,(: return explode($1,"/")[<1]; :));
702 list=map(m_indices(mkmapping(list)),(: return "/sys/"+$1; :));
703 list=filter(list,(: return file_size($1)>0; :));
MG Mud User88f12472016-06-24 23:31:02 +0200704 list=sort_array(list, #'<);
705 ausgabe="";
706 for (i=sizeof(list);i--;)
707 {
MG Mud User88f12472016-06-24 23:31:02 +0200708 str=implode(filter(explode(read_file(list[i]),"\n"),
709 (: return $1[0..9]=="#define P_";:)),"\n");
MG Mud User88f12472016-06-24 23:31:02 +0200710 if (str!="") ausgabe+=sprintf("%s\n%s\n\n", list[i], str);
711 }
712 if (ausgabe!="")
713 More(ausgabe);
714 else
715 printf("Keine Property-Definitionen zu diesem Objekt gefunden!\n");
716 return 1;
717}
718
719// ########
720//############################### GREP ###################################
721// ########
722
MG Mud User88f12472016-06-24 23:31:02 +0200723//
724// grep_file: Datei greppen
725// rexpr: Regular Expression
726// flags: Flags
727//
728
729private int grep_file(mixed filedata, string rexpr, int flags)
730{
Zesstrac70bf582019-11-26 00:43:17 +0100731 string fullname=filedata[FULLNAME];
MG Mud User88f12472016-06-24 23:31:02 +0200732 if ((flags&GREP_F)&&fullname=="/players/"+getuid()+"/grep.out")
Zesstraefad7be2018-11-06 23:18:30 +0100733 {
734 write_file("/players/"+getuid()+"/grep.out",
735 "Uebergehe grep.out ...\n");
736 return RET_FAIL;
MG Mud User88f12472016-06-24 23:31:02 +0200737 }
738 switch(filedata[FILESIZE])
739 {
Zesstraefad7be2018-11-06 23:18:30 +0100740 case FSIZE_DIR: return RET_FAIL;
741 case FSIZE_NOFILE: return ERROR(DOESNT_EXIST,fullname,RET_FAIL);
MG Mud User88f12472016-06-24 23:31:02 +0200742 case 0: return RET_FAIL;
743 default: break;
744 }
Zesstraefad7be2018-11-06 23:18:30 +0100745 if (!MAY_READ(fullname))
746 return ERROR(NO_READ,fullname,RET_FAIL);
Zesstrac70bf582019-11-26 00:43:17 +0100747
748 // Bei case-insensitiver Suche das Pattern in Kleinschreibung wandeln
MG Mud User88f12472016-06-24 23:31:02 +0200749 if (flags&GREP_I)
750 rexpr=lower_case(rexpr);
Zesstrac70bf582019-11-26 00:43:17 +0100751
752 // Portionsweise das komplette File einlesen.
753 int maxlen = query_limits()[LIMIT_BYTE];
754 int ptr;
755 bytes read_buffer;
756 bytes data = b"";
757 while (sizeof(read_buffer = read_bytes(fullname, ptr, maxlen)))
MG Mud User88f12472016-06-24 23:31:02 +0200758 {
Zesstrac70bf582019-11-26 00:43:17 +0100759 data += read_buffer;
760 ptr += sizeof(read_buffer);
761 // die Schleifenbedingung erkennt zwar auch, wenn das File vollstaendig
762 // ist, aber wir koennen den Speicherzugriff auch einsparen und schauen,
763 // ob wir schon alles haben.
764 if (ptr >= filedata[FILESIZE])
765 break;
MG Mud User88f12472016-06-24 23:31:02 +0200766 }
Zesstrac70bf582019-11-26 00:43:17 +0100767 // In string konvertieren, wir gehen davon aus, das File ist UTF8-kodiert.
768 string text = to_text(data, "UTF-8");
769 string *lines = explode(text, "\n");
770 int count; // Anzahl Zeilen mit Treffern
771 <string|string*> result = ({}); // zutreffende Zeilen
772 int linecount = 1;
773 foreach(string line: lines)
MG Mud User88f12472016-06-24 23:31:02 +0200774 {
Zesstrac70bf582019-11-26 00:43:17 +0100775 string orig_line = line;
776 // Suche case-insensitive?
777 if (flags&GREP_I)
778 line = lower_case(line);
779 int match = regmatch(line, rexpr) != 0;
780 if (flags&GREP_V) match = !match; // Match ggf. invertieren
MG Mud User88f12472016-06-24 23:31:02 +0200781 if (match)
782 {
Zesstrac70bf582019-11-26 00:43:17 +0100783 // Ausgeben oder nicht?
784 if (!(flags&GREP_C))
MG Mud User88f12472016-06-24 23:31:02 +0200785 {
Zesstrac70bf582019-11-26 00:43:17 +0100786 // Mit Zeilennummer?
787 if (flags&GREP_N)
788 result+=({ sprintf("%4d %s", linecount, orig_line)});
MG Mud User88f12472016-06-24 23:31:02 +0200789 else
Zesstrac70bf582019-11-26 00:43:17 +0100790 result+=({orig_line});
MG Mud User88f12472016-06-24 23:31:02 +0200791 }
Zesstrac70bf582019-11-26 00:43:17 +0100792 ++count;
MG Mud User88f12472016-06-24 23:31:02 +0200793 }
Zesstrac70bf582019-11-26 00:43:17 +0100794 ++linecount;
MG Mud User88f12472016-06-24 23:31:02 +0200795 }
Zesstrac70bf582019-11-26 00:43:17 +0100796
MG Mud User88f12472016-06-24 23:31:02 +0200797 if (count)
798 {
Zesstrac70bf582019-11-26 00:43:17 +0100799 // Bei -h werden die Dateinamen unterdrueckt.
800 if (flags&GREP_H)
801 fullname="";
MG Mud User88f12472016-06-24 23:31:02 +0200802 else
Zesstrac70bf582019-11-26 00:43:17 +0100803 fullname=sprintf("%s ",fullname);
804
805 if (flags&GREP_C)
806 result=sprintf("%s%d passende Zeile%s.\n",fullname, count,
807 (count==1?"":"n"));
808 else
809 result = ( (sizeof(fullname) ? fullname + "\n" : "")
810 + implode(result,"\n") + "\n");
Zesstrac70bf582019-11-26 00:43:17 +0100811
Zesstra77c0bf22020-01-07 22:27:27 +0100812 // Ergebnis ausgeben in File oder an Magier
813 if (flags&GREP_F)
814 return write_file("/players/"+getuid()+"/grep.out",result);
815 write(result);
816 }
MG Mud User88f12472016-06-24 23:31:02 +0200817 return RET_OK;
818}
819
820static int _grep(string cmdline)
821{
822 string rexpr,mask;
823 mixed *args;
824 int flags;
825 cmdline=_unparsed_args();
826 args=parseargs(cmdline,&flags,GREP_OPTS,0);
827 if ((flags==-1)||!sizeof(args))
828 return USAGE("grep [-" GREP_OPTS
829 "] <regexp> <datei/verz> [<datei2> ... ] [<maske>]");
830 rexpr=args[0];
831 if (catch(regexp(({""}),rexpr))||!regexp(({""}),rexpr))
832 return printf("grep: Ungueltiger Suchausdruck: %s\n",rexpr) ,1;
Zesstracc3f2502018-11-14 23:46:47 +0100833 args=(sizeof(args)>1 ? args[1..] : ({}) );
MG Mud User88f12472016-06-24 23:31:02 +0200834 if (flags&GREP_M)
835 {
836 mask=args[<1];
Zesstracc3f2502018-11-14 23:46:47 +0100837 if (sizeof(args) > 2)
838 args = (sizeof(args) > 1 ? args[0..<2] : ({}) );
MG Mud User88f12472016-06-24 23:31:02 +0200839 }
840 if (!sizeof(args))
841 return USAGE("grep [-" GREP_OPTS
842 "] <regexp> <datei/verz> [<datei2> ... ] [<maske>]");
843 args=map(args,#'to_filename)-({0});
MG Mud User88f12472016-06-24 23:31:02 +0200844 args=file_list(args,MODE_GREP,(flags&GREP_R?1:0),"/",mask);
845 if (!sizeof(args))
846 return printf("Keine passenden Dateien gefunden.\n"),1;
847 if (flags&GREP_I) rexpr=lower_case(rexpr);
848 if (flags&GREP_F)
849 {
Zesstraefad7be2018-11-06 23:18:30 +0100850 if (file_size("/players/"+getuid()+"/grep.out")==FSIZE_DIR
851 || !MAY_WRITE("/players/"+getuid()+"/grep.out"))
MG Mud User88f12472016-06-24 23:31:02 +0200852 return printf("grep: Datei /players/%s/grep.out kann nicht "
Zesstraefad7be2018-11-06 23:18:30 +0100853 "geschrieben werden.\n",getuid()),1;
MG Mud User88f12472016-06-24 23:31:02 +0200854 else
855 write_file("/players/"+getuid()+"/grep.out",
856 "Ausgabe von \"grep " + _unparsed_args() + "\":\n");
857 }
858 asynchron(args,#'grep_file,rexpr,flags,0);
859 return 1;
860}