blob: 00a3b64d52618d996ee3eed363737345b6a88162 [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
128private string _ls_output_short(mixed filedata,
129 int maxlen,int counter,int maxcount)
130{
131 string tmp;
132 tmp=filedata[BASENAME];
133 maxlen-=sizeof(tmp);
134 switch(filedata[FILESIZE])
135 {
Zesstraa2bb5bf2021-04-20 17:45:22 +0200136 case FSIZE_DIR: tmp=sprintf(lstemplates.templates[DIR],tmp);
137 maxlen-=lstemplates.templates[DIR,1]; break;
138 case FSIZE_NOFILE: tmp=sprintf(lstemplates.templates[VC],tmp);
139 maxlen-=lstemplates.templates[VC,1]; break;
MG Mud User88f12472016-06-24 23:31:02 +0200140 default: if (find_object(filedata[FULLNAME]))
141 {
Zesstraa2bb5bf2021-04-20 17:45:22 +0200142 maxlen-=lstemplates.templates[OBJ,1];
143 tmp=sprintf(lstemplates.templates[OBJ],tmp); break;
MG Mud User88f12472016-06-24 23:31:02 +0200144 }
145 }
Zesstraa391ccf2022-06-06 17:24:07 +0200146 if (!maxcount) return terminal_colour(tmp+"\n",lstemplates.colormap);
Zesstraa2bb5bf2021-04-20 17:45:22 +0200147 return terminal_colour(sprintf("%-*s%s",(maxlen+sizeof(tmp)),tmp,
148 ((counter++)==maxcount?(counter=0,"\n"):" ")),
149 lstemplates.colormap);
MG Mud User88f12472016-06-24 23:31:02 +0200150}
151
152private int _ls_maxlen(mixed filedata,int flags,int maxlen)
153{
154 string base;
MG Mud User88f12472016-06-24 23:31:02 +0200155 base=filedata[BASENAME];
156 if (!(flags&LS_A)&&(base[0]=='.')) return 0;
MG Mud User88f12472016-06-24 23:31:02 +0200157 maxlen=max(maxlen,sizeof(base));
MG Mud User88f12472016-06-24 23:31:02 +0200158 return 1;
159}
160
161private string _ls_output_long(mixed filedata, int flags,closure valid_read,
162 closure valid_write,closure creator_file)
163{
Zesstraefad7be2018-11-06 23:18:30 +0100164 string base=filedata[BASENAME];
165 if (!(sizeof(base)) || ( (!(flags&LS_A)) && (base[0]=='.')) )
MG Mud User88f12472016-06-24 23:31:02 +0200166 return 0;
Zesstraefad7be2018-11-06 23:18:30 +0100167 int size=filedata[FILESIZE];
168 string path=filedata[PATHNAME];
Zesstraf22b6a82018-11-07 22:39:55 +0100169 string full=filedata[FULLNAME];
Zesstraefad7be2018-11-06 23:18:30 +0100170 int dir=(size==FSIZE_DIR);
171 object ob=find_object(full);
172 int ftime=filedata[FILEDATE];
173 string date;
Bugfix59cb0e52019-09-06 15:17:29 +0200174 if ((time()-ftime)>31536000) // ein Jahr
Zesstraa2bb5bf2021-04-20 17:45:22 +0200175 date=strftime("%b %e %Y", ftime);
Zesstraefad7be2018-11-06 23:18:30 +0100176 else
177 date=strftime("%b %e %H:%M", ftime);
178
179 string creator="";
180 string group="";
MG Mud User88f12472016-06-24 23:31:02 +0200181 if (flags&LS_U)
182 {
Vanion50652322020-03-10 21:13:25 +0100183 creator=({string})call_other(master(),"creator_file", full);
MG Mud User88f12472016-06-24 23:31:02 +0200184 switch(creator)
185 {
186 case ROOTID: creator="root"; break;
Zesstraefad7be2018-11-06 23:18:30 +0100187 case BACKBONEID: creator="std"; break;
188 case MAILID: creator="mail"; break;
189 case NEWSID: creator="news"; break;
190 case NOBODY: creator="nobody"; break;
191 case POLIZEIID: creator="polizei"; break;
192 case DOCID: creator="doc"; break;
193 case GUILDID: creator="gilde"; break;
194 case ITEMID: creator="items"; break;
MG Mud User88f12472016-06-24 23:31:02 +0200195 default: if(!creator) creator="none"; break;
196 }
197 }
198 if (flags&LS_G)
199 {
Zesstraf22b6a82018-11-07 22:39:55 +0100200 string *tmp=explode(path, "/") - ({""});
MG Mud User88f12472016-06-24 23:31:02 +0200201 if (sizeof(tmp))
202 {
203 switch(tmp[0])
204 {
205 case WIZARDDIR: group="magier"; break;
Zesstraefad7be2018-11-06 23:18:30 +0100206 case NEWSDIR: group="news"; break;
207 case MAILDIR: group="mail"; break;
208 case FTPDIR: group="public"; break;
209 case PROJECTDIR: group="project"; break;
MG Mud User88f12472016-06-24 23:31:02 +0200210 case DOMAINDIR: if (sizeof(tmp)>1) { group=tmp[1]; break; }
211 default: group="mud"; break;
212 }
213 }
214 else group="mud";
215 }
Zesstraa2bb5bf2021-04-20 17:45:22 +0200216 if (dir) base=sprintf(lstemplates.templates[DIR],base);
MG Mud User88f12472016-06-24 23:31:02 +0200217 else
218 {
219 if (ob)
220 {
Zesstraefad7be2018-11-06 23:18:30 +0100221 if (size==FSIZE_NOFILE)
Zesstraa2bb5bf2021-04-20 17:45:22 +0200222 base=sprintf(lstemplates.templates[VC],base);
MG Mud User88f12472016-06-24 23:31:02 +0200223 else
Zesstraa2bb5bf2021-04-20 17:45:22 +0200224 base=sprintf(lstemplates.templates[OBJ],base);
MG Mud User88f12472016-06-24 23:31:02 +0200225 }
226 }
Zesstraa2bb5bf2021-04-20 17:45:22 +0200227 return terminal_colour(
228 sprintf(("%c%c%c%c %3d" + ((flags&LS_U) ? " %-24.24s" : "%-0.1s")
Zesstraefad7be2018-11-06 23:18:30 +0100229 +((flags&LS_G) ? " %-8.8s" : "%0.1s") + " %8s %s %s\n"),
230 (dir ? 'd' : '-'),
MG Mud User88f12472016-06-24 23:31:02 +0200231 (!funcall(valid_read,full,getuid(),
Zesstraefad7be2018-11-06 23:18:30 +0100232 "read_file",this_object()) ? '-' : 'r'),
MG Mud User88f12472016-06-24 23:31:02 +0200233 (!funcall(valid_write,full,getuid(),
Zesstraefad7be2018-11-06 23:18:30 +0100234 "write_file",this_object()) ? '-' : 'w'),
235 (ob ? 'x' : '-'),
236 (dir ? (sizeof((get_dir(full+"/*")||({}))-({".",".."}))) : 0),
237 creator, group,
238 (dir ? "-" : size==FSIZE_NOFILE ? "<vc>" : to_string(size)),
Zesstraa2bb5bf2021-04-20 17:45:22 +0200239 date, base),
240 lstemplates.colormap);
MG Mud User88f12472016-06-24 23:31:02 +0200241}
242
243
244static int _ls(string cmdline)
245{
246 int flags,cmp,i,arg_size,size;
247 int maxlen,counter,maxcount;
Arathorna8ae8662019-11-25 19:01:19 +0100248 mixed* args;
249 string output;
MG Mud User88f12472016-06-24 23:31:02 +0200250 mixed *tmp;
Arathornb3051452021-05-13 21:13:03 +0200251 closure v_read,v_write,creator,sort_fun;
MG Mud User88f12472016-06-24 23:31:02 +0200252
253 cmdline=_unparsed_args()||"";
254 args=parseargs(cmdline,&flags,LS_OPTS,1);
255 if (flags==-1)
256 return USAGE("ls [-" LS_OPTS "] [<Verzeichnis>] [<Verzeichnis2> ..]");
257 SetColorstrings();
258 output="";
259 if (!sizeof(args)) args=({ QueryProp(P_CURRENTDIR) });
260 if (!sizeof(args=file_list(args,MODE_LSA,0,"/")))
261 return notify_fail("ls: Keine passenden Verzeichnisse gefunden.\n"), 0;
262// Files sortieren die erste
263 if (flags&LS_T) cmp=FILEDATE;
264 else if (flags&LS_S) cmp=FILESIZE;
265 else cmp=BASENAME; // =0 :-)
Arathorn06b52922018-11-26 20:47:10 +0100266
267 if ( !cmp && !(flags&LS_R) || cmp && (flags&LS_R) )
268 sort_fun = function int (mixed* a, mixed* b) {
269 return (a[cmp] > b[cmp]);
270 };
271 else
272 sort_fun = function int (mixed* a, mixed* b) {
273 return (a[cmp] < b[cmp]);
274 };
MG Mud User88f12472016-06-24 23:31:02 +0200275 args=sort_array(args,sort_fun);
276// Ausgabeformat bestimmen
277 if (flags&LS_L)
278 {
279 v_read=VALID_READ_CL;
280 v_write=VALID_WRITE_CL;
281 creator=CREATOR_CL;
282 }
283 arg_size=sizeof(args);
284 if (arg_size>1||(arg_size&&args[0][FILESIZE]>=0))
285 {
286 if (flags&LS_L)
287 tmp=map(args,#'_ls_output_long,flags,v_read,
288 v_write,creator)-({0});
289 else
290 {
291 counter=0;maxlen=0;
292 tmp=filter(args,#'_ls_maxlen,flags,&maxlen);
293 if (maxlen>76) maxlen=76;
294 maxcount=(78/(maxlen+2))-1;
295 tmp=map(tmp,#'_ls_output_short,maxlen,&counter,maxcount);
296 }
297 output=sprintf("\n%d Dateien/Unterverzeichnisse:\n%s\n",
298 sizeof(tmp),implode(tmp,""));
299 }
300 for(i=0;i<arg_size;i++)
301 {
302 tmp=({});
303 size=args[i][FILESIZE];
Zesstraefad7be2018-11-06 23:18:30 +0100304 if (size==FSIZE_DIR)
MG Mud User88f12472016-06-24 23:31:02 +0200305 {
306 tmp=file_list(({args[i][FULLNAME]+"/*"}),MODE_LSB,0,"/");
307 tmp=sort_array(tmp,sort_fun);
308 if (flags&LS_L)
309 tmp=map(tmp,#'_ls_output_long,flags,v_read,
310 v_write,creator)-({0});
311 else
312 {
313 counter=0;maxlen=0;
314 tmp=filter(tmp,#'_ls_maxlen,flags,&maxlen);
315 maxcount=(78/(maxlen+2));
316 if (maxcount) maxcount--;
317 tmp=map(tmp,#'_ls_output_short,maxlen,&counter,maxcount);
318 }
319 if (sizeof(tmp))
320 {
321 output+=sprintf("\n%s: Verzeichnis, %d Dateien/Unterverzeichnisse:\n",
322 args[i][FULLNAME],sizeof(tmp));
323 output+=(implode(tmp,"")+((string)(tmp[<1][<1..<1])=="\n"?"":"\n"));
324 }
325 else
326 {
327 output+=sprintf("\n%s: Leeres Verzeichnis.\n",args[i][FULLNAME]);
328 }
329 }
330 }
331 More(output);
332 return 1;
333}
334
335// ########
336//############################### MORE ###################################
337// ########
338//
339// _more_file: Mehrere Files hintereinander ausgeben
340//
341
342private void _more_file(string *arg)
343{
344 if (!sizeof(arg)) return;
345 printf("more: Naechste Datei: %s\n",arg[0]);
Zesstra3de3a032018-11-08 19:17:03 +0100346 More(arg[0],1,#'_more_file,
347 (sizeof(arg)>1 ? ({ arg[1..]}) : ({})) );
MG Mud User88f12472016-06-24 23:31:02 +0200348 return;
349}
350
351
352private mixed _size_filter(mixed *arg)
353{
354 if (arg[FILESIZE]>0) return arg[FULLNAME];
355 if (arg[FILESIZE]==0)
356 {
357 printf("%s: %s: Leere Datei.\n",query_verb()||"more",arg[FULLNAME]);
358 return 0;
359 }
Zesstraefad7be2018-11-06 23:18:30 +0100360 if (arg[FILESIZE]==FSIZE_DIR)
MG Mud User88f12472016-06-24 23:31:02 +0200361 printf("%s: %s ist ein Verzeichnis.\n",query_verb()||"more",arg[FULLNAME]);
362 else
363 printf("%s: %s: Datei existiert nicht.\n", query_verb()||"more",
364 arg[FULLNAME]);
365 return 0;
366}
367
368
369//
370// _more: Dateien anzeigen
371// cmdline: Kommandozeile
372//
373
374static int _more(string cmdline)
375{
376 mixed *args;
377 int flags;
378 cmdline=_unparsed_args();
379 args=parseargs(cmdline,&flags,"",1);
380 if (flags==-1||!sizeof(args)) return USAGE("more <datei> [<datei2>..]");
381 args=file_list(args,MODE_MORE,0,"/");
382 if (!sizeof(args))
383 return printf("more: %s: Keine passende Datei gefunden.\n",cmdline),1;
384 args=map(args,#'_size_filter)-({0});
385 if (sizeof(args)) _more_file(args);
386 return 1;
387}
388
389// ###################
390//########################## HEAD, TAIL, CAT #############################
391// ###################
392
393static int _cat(string cmdline)
394{
395 mixed *args;
396 int flags;
397 cmdline=_unparsed_args();
398 args=parseargs(cmdline,&flags,"",1);
399 if(flags==-1||!sizeof(args))
400 return USAGE(query_verb()+" <dateiname> [<datei2>..]");
401 args=file_list(args,MODE_CAT,0,"/");
402 if (!sizeof(args))
403 return printf("%s: %s: Keine passende Datei gefunden.\n",query_verb(),
404 cmdline),1;
405 args=map(args,#'_size_filter)-({0});
406 if (!sizeof(args)) return 1;
407 while(sizeof(args))
408 {
409 switch(query_verb())
410 {
411 case "cat":
412 if (!cat(args[0]))
413 printf("cat: %s konnte nicht angezeigt werden.\n",args[0]);
414 break;
415 case "head":
416 if (!cat(args[0],0,10))
417 printf("head: %s konnte nicht angezeigt werden.\n",args[0]);
418 break;
419 case "tail": tail(args[0]); break;
420 }
Zesstra3de3a032018-11-08 19:17:03 +0100421 if (sizeof(args) > 1)
422 args=args[1..];
423 else
424 break;
MG Mud User88f12472016-06-24 23:31:02 +0200425 }
426 return 1;
427}
428
429// #######
430//############################### MAN ###################################
431// #######
432
Arathorn99c40582020-08-28 19:46:23 +0200433/* Sortiert ein Array paarweise, wobei von jedem Wertepaar der erste Wert
434 fuer die Sortierung herangezogen wird. */
435private string* SortInPairs(string* arr) {
436 if (!sizeof(arr))
437 return arr;
438
439 string** helper = ({});
440
441 /* In Sub-Arrays zerlegen, die jeweils ein Wertepaart enthalten:
442 ({s0, s1, s2, s3}) => ({({s0,s1}), ({s2,s3})}) */
443 int listsize = sizeof(arr);
444 for(int i ; i<listsize ; i+=2) {
445 helper += ({ arr[i..i+1] });
446 }
447
448 // Nach dem ersten Element jedes Sub-Arrays sortieren.
449 helper = sort_array(helper, function int (string* h1, string* h2) {
450 return lower_case(h1[0]) > lower_case(h2[0]);
451 });
452
453 // Eingabe-Array loeschen und aus den sortierten Wertepaaren neu aufbauen.
454 arr = ({});
455 foreach(string* h : helper) {
456 arr += h;
457 }
458
459 return arr;
460}
461
MG Mud User88f12472016-06-24 23:31:02 +0200462static int _man(string cmdline)
463{
MG Mud User88f12472016-06-24 23:31:02 +0200464 int i, flags;
Arathorndb201cc2020-08-28 17:02:38 +0200465 string *input;
MG Mud User88f12472016-06-24 23:31:02 +0200466
Arathorn1facd5f2020-08-28 16:41:17 +0200467 string* args = parseargs(_unparsed_args(), &flags, MAN_OPTS, 0);
468
MG Mud User88f12472016-06-24 23:31:02 +0200469 if (flags==-1 ||
470 (sizeof(args)!=1 &&
471 (sizeof(args)<2 || sizeof(args[1])>1 || !(i=to_int(args[1])))))
472 return USAGE("man [-" MAN_OPTS "] <hilfeseite>");
MG Mud User88f12472016-06-24 23:31:02 +0200473
Arathorn72c7e542020-08-28 16:50:28 +0200474 input = explode(args[0], "/");
475
Arathorn815f2742020-08-28 17:29:04 +0200476 /* Wenn das Ergebnis einer vorherigen Suche noch vorliegt und die aktuelle
477 Eingabe als einziges eine einstellige Zahl enthaelt, und diese dann in
478 dem alten Suchergebnis enthalten ist, wird die Eingabe durch das alte
479 Ergebnis ersetzt. <i> wird in dem Fall geloescht.
480 Wenn kein altes Ergebnis gefunden wurde, enthaelt <i> die eingegebene
481 Nummer. */
Arathorn72c7e542020-08-28 16:50:28 +0200482 if (oldman_result && sizeof(input)==1 && sizeof(args)==1 &&
483 sizeof(input[0])==1 && (i=to_int(input[0])) &&
Arathornd0a1e662020-08-28 17:01:24 +0200484 member(oldman_result,i))
485 {
Arathorn72c7e542020-08-28 16:50:28 +0200486 input = ({oldman_result[i,0], oldman_result[i,1]});
Arathornd0a1e662020-08-28 17:01:24 +0200487 i = 0;
MG Mud User88f12472016-06-24 23:31:02 +0200488 }
Arathorn815f2742020-08-28 17:29:04 +0200489 /* Ansonsten wenn weder -m, noch -r gesetzt sind und es eine Manpage gibt,
490 die genau der Eingabe entspricht, wird diese verwendet. */
Arathorn72c7e542020-08-28 16:50:28 +0200491 else if (!(flags&(MAN_M|MAN_R)) && sizeof(input)>1)
MG Mud User88f12472016-06-24 23:31:02 +0200492 {
Arathornd0a1e662020-08-28 17:01:24 +0200493 if (file_size(MAND_DOCDIR+args[0]) >= 0)
Arathorn72c7e542020-08-28 16:50:28 +0200494 input = ({input[<1], args[0]});
MG Mud User88f12472016-06-24 23:31:02 +0200495 else
Arathorn72c7e542020-08-28 16:50:28 +0200496 input = ({});
MG Mud User88f12472016-06-24 23:31:02 +0200497 }
498 else
499 {
Arathorn815f2742020-08-28 17:29:04 +0200500 /* Soll eine Regexp-Suche durchgefuehrt werden? Dann erstmal den Ausdruck
501 auf Gueltigkeit pruefen. */
MG Mud User88f12472016-06-24 23:31:02 +0200502 if (flags&MAN_R)
503 {
Arathornd0a1e662020-08-28 17:01:24 +0200504 flags &= (~MAN_M);
505 if (catch(regexp(({""}), args[0])) || !regexp(({""}), args[0]))
506 {
507 printf("man: Ungueltiger Ausdruck in Maske.\n");
508 return 1;
509 }
MG Mud User88f12472016-06-24 23:31:02 +0200510 }
Arathorn815f2742020-08-28 17:29:04 +0200511 /* Die Ausgabe von locate() liefert ein String-Array, das abwechselnd den
512 Dateinamen der gefundenen Manpage und den vollen Pfad dieser Manpage
513 unterhalb von /doc enthaelt. Beispielsweise ({"Defend","lfun/Defend"})
514 */
Arathorn72c7e542020-08-28 16:50:28 +0200515 input = ({string *})MAND->locate(args[0], flags&(MAN_M|MAN_R));
Arathorn84dd5f22020-08-28 17:24:44 +0200516 // Sortierung case-insensitive, ggf. vorhandene Pfade dabei ignorieren
517 // Wird fuer die spaetere Ausgabe der Liste benoetigt.
Arathorn99c40582020-08-28 19:46:23 +0200518 input = SortInPairs(input);
MG Mud User88f12472016-06-24 23:31:02 +0200519 }
520
Arathorn815f2742020-08-28 17:29:04 +0200521 /* Alte Such-Treffer werden entsorgt. */
Arathornd0a1e662020-08-28 17:01:24 +0200522 oldman_result = 0;
523
Arathorn815f2742020-08-28 17:29:04 +0200524 /* <i> kann maximal eine einstellige Zahl sein, 1-9, wenn eine solche als
525 einziges Argument eingegeben wurde und kein passendes Ergebnis in einer
526 vorigen Suchanfrage gefunden wurde.
527
528 <input> kann nur genau dann mehr als 2 Elemente haben, wenn das Ergebnis
529 des Aufrufs MAND->locate() drinsteht. Und nur dann muss ueberhaupt ein
530 hoeheres Wertepaar rausgeschnitten werden, denn wenn <input> bis zu 2
531 Elemente hat, kann damit direkt weitergearbeitet werden.
532
533 Beispiel: bei einer Eingabe von "4" wird hier aus <input> das 7. und
534 8. Element entnommen (Indizes [6..7]). */
Arathorn131bb742020-08-28 17:05:02 +0200535 if(i && sizeof(input)>2 && sizeof(input) >= i*2)
536 input = input[(i*2-2)..(i*2-1)];
Arathornd0a1e662020-08-28 17:01:24 +0200537
Arathorn72c7e542020-08-28 16:50:28 +0200538 switch (sizeof(input))
MG Mud User88f12472016-06-24 23:31:02 +0200539 {
Arathorn815f2742020-08-28 17:29:04 +0200540 /* <input> leer, nichts brauchbares gefunden. */
MG Mud User88f12472016-06-24 23:31:02 +0200541 case 0:
Arathornd0a1e662020-08-28 17:01:24 +0200542 printf("Keine Hilfeseite gefunden fuer '%s'.\n", args[0]);
MG Mud User88f12472016-06-24 23:31:02 +0200543 break;
Arathornd0a1e662020-08-28 17:01:24 +0200544
Arathorn815f2742020-08-28 17:29:04 +0200545 /* Genau 2 Elemente enthalten? Dann kann das direkt ausgegeben werden. */
MG Mud User88f12472016-06-24 23:31:02 +0200546 case 2:
547 /*
548 if (flags&MAN_I)
549 {
550 // BRANCH TO MANUALD
551 return 1;
552 }
553 */
Arathorn72c7e542020-08-28 16:50:28 +0200554 printf("Folgende Hilfeseite wurde gefunden: %s\n", input[1]);
555 More(MAND_DOCDIR+input[1], 1);
MG Mud User88f12472016-06-24 23:31:02 +0200556 return 1;
Arathorn815f2742020-08-28 17:29:04 +0200557
558 /* Alles andere: */
MG Mud User88f12472016-06-24 23:31:02 +0200559 default:
Arathorn131bb742020-08-28 17:05:02 +0200560 i = sizeof(input)/2;
Arathorndb201cc2020-08-28 17:02:38 +0200561 string* output = allocate(i);
Arathorn815f2742020-08-28 17:29:04 +0200562
563 // Inhalt: ([ int nummer : string manpage; string manpage_pfad ])
Arathornd0a1e662020-08-28 17:01:24 +0200564 oldman_result = m_allocate(i, 2);
Arathorn815f2742020-08-28 17:29:04 +0200565
Arathornd0a1e662020-08-28 17:01:24 +0200566 while (i)
MG Mud User88f12472016-06-24 23:31:02 +0200567 {
Arathorn131bb742020-08-28 17:05:02 +0200568 output[i-1] = input[i*2-2];
Arathorn1719b252020-08-28 19:44:32 +0200569 m_add(oldman_result, i, input[i*2-2], input[i*2-1]);
MG Mud User88f12472016-06-24 23:31:02 +0200570 i--;
571 }
Arathorndb5d16d2020-05-11 01:23:18 +0200572
Arathorndb5d16d2020-05-11 01:23:18 +0200573 // Numerierung ergaenzen
Arathorndb201cc2020-08-28 17:02:38 +0200574 foreach(int j : sizeof(output))
Arathornd0a1e662020-08-28 17:01:24 +0200575 {
Arathorndb201cc2020-08-28 17:02:38 +0200576 output[j] = sprintf("%d: %s", j+1, output[j]);
Arathorndb5d16d2020-05-11 01:23:18 +0200577 }
578
579 int tty_cols = QueryProp(P_TTY_COLS)-2;
580 string list = "Es wurden die folgenden, potentiell passenden Seiten "
581 "gefunden:\n";
582
583 // Wer keine Grafik sehen will, bekommt eine andere Anzeige.
Arathornd0a1e662020-08-28 17:01:24 +0200584 if (QueryProp(P_NO_ASCII_ART))
585 {
Arathorndb5d16d2020-05-11 01:23:18 +0200586 // @ als geschuetztes Leerzeichen verwenden, um einen Umbruch
587 // nach den Nummern zu verhindern.
Arathorndb201cc2020-08-28 17:02:38 +0200588 output = map(output, #'regreplace, ": ", ":@", 1);
589 list += break_string(implode(output, " "), tty_cols);
Arathorndb5d16d2020-05-11 01:23:18 +0200590 list = regreplace(list, ":@", ": ", 1);
591 }
Arathornd0a1e662020-08-28 17:01:24 +0200592 else
593 {
Arathorndb5d16d2020-05-11 01:23:18 +0200594 // Anzahl Spalten ausrechnen: Terminalbreite / Laenge des laengsten
Arathorndb201cc2020-08-28 17:02:38 +0200595 // Elements in <output>. Kann der Spaltenmodus von sprintf() an sich
Arathorndb5d16d2020-05-11 01:23:18 +0200596 // selbst, das liefert aber nicht immer so guenstige Ergebnisse.
Arathorndb201cc2020-08-28 17:02:38 +0200597 int maxwidth = max(map(output, #'sizeof));
Arathorndb5d16d2020-05-11 01:23:18 +0200598 int tablecols = tty_cols/maxwidth;
599 list += "-"*tty_cols+"\n"+
Arathorndb201cc2020-08-28 17:02:38 +0200600 sprintf("%#-*.*s\n", tty_cols, tablecols, implode(output,"\n"))+
Arathorndb5d16d2020-05-11 01:23:18 +0200601 "-"*tty_cols+"\n";
602 }
603 printf(list);
MG Mud User88f12472016-06-24 23:31:02 +0200604 break;
605 }
606 return 1;
607}
608
609// ########
610//############################### RMAN ##################################
611// ########
612
613static int _rman(string str)
614{
615 int flags;
616 string *args;
617
618 str=_unparsed_args();
619 args=parseargs(str,&flags,"",0);
620 if (flags==-1||sizeof(args)!=2)
621 return USAGE("rman <hilfeseite> <mudname>");
622 write("man: " +
Vanion50652322020-03-10 21:13:25 +0100623 ({string})call_other(UDP_CMD_DIR+"man","send_request",args[1],args[0]));
MG Mud User88f12472016-06-24 23:31:02 +0200624 return 1;
625}
626
627
628// #############
629//############################# SHOWPROPS ###############################
630// #############
631
632//
633// _showprops: Zeigt Properties zu einem Objekt an
634//
635
636static int _showprops(string str)
637{
638 int i;
639 string *list, ausgabe;
640
641 if (str) {
642 if (str[0]!='/') str="/"+str;
643 if (str[0..4]!="/std/")
644 {
645 printf("showprops: Es koennen nur Properties von Objekten "
646 "aus /std/ angezeigt werden.\n");
647 return 1;
648 }
649 if (str[<1]=='.') str+="c";
650 if (str[<2..<1]!=".c") str+=".c";
651 if (file_size(str)<0)
652 {
653 printf("showprops: %s: Es gibt kein Objekt diesen Namens.\n",str[0..<3]);
654 return 1;
655 }
Zesstraefad7be2018-11-06 23:18:30 +0100656 if (catch(load_object(str)))
MG Mud User88f12472016-06-24 23:31:02 +0200657 {
658 printf("showprops: %s: Datei konnte nicht geladen werden.\n",str);
659 return 1;
660 }
661 }
662 if (!str || !find_object(str)) {
663 notify_fail("Welche Properties moechtest Du sehen?"
664 " Bsp.: <showprops /std/npc>\n");
665 return 0;
666 }
667 list=inherit_list(find_object(str));
MG Mud User88f12472016-06-24 23:31:02 +0200668 list=map(list,(: return $1[5..<2]+"h"; :));
669 list+=map(list,(: return explode($1,"/")[<1]; :));
670 list=map(m_indices(mkmapping(list)),(: return "/sys/"+$1; :));
671 list=filter(list,(: return file_size($1)>0; :));
MG Mud User88f12472016-06-24 23:31:02 +0200672 list=sort_array(list, #'<);
673 ausgabe="";
674 for (i=sizeof(list);i--;)
675 {
MG Mud User88f12472016-06-24 23:31:02 +0200676 str=implode(filter(explode(read_file(list[i]),"\n"),
677 (: return $1[0..9]=="#define P_";:)),"\n");
MG Mud User88f12472016-06-24 23:31:02 +0200678 if (str!="") ausgabe+=sprintf("%s\n%s\n\n", list[i], str);
679 }
680 if (ausgabe!="")
681 More(ausgabe);
682 else
683 printf("Keine Property-Definitionen zu diesem Objekt gefunden!\n");
684 return 1;
685}
686
687// ########
688//############################### GREP ###################################
689// ########
690
MG Mud User88f12472016-06-24 23:31:02 +0200691//
692// grep_file: Datei greppen
693// rexpr: Regular Expression
694// flags: Flags
695//
696
697private int grep_file(mixed filedata, string rexpr, int flags)
698{
Zesstrac70bf582019-11-26 00:43:17 +0100699 string fullname=filedata[FULLNAME];
MG Mud User88f12472016-06-24 23:31:02 +0200700 if ((flags&GREP_F)&&fullname=="/players/"+getuid()+"/grep.out")
Zesstraefad7be2018-11-06 23:18:30 +0100701 {
702 write_file("/players/"+getuid()+"/grep.out",
703 "Uebergehe grep.out ...\n");
704 return RET_FAIL;
MG Mud User88f12472016-06-24 23:31:02 +0200705 }
706 switch(filedata[FILESIZE])
707 {
Zesstraefad7be2018-11-06 23:18:30 +0100708 case FSIZE_DIR: return RET_FAIL;
709 case FSIZE_NOFILE: return ERROR(DOESNT_EXIST,fullname,RET_FAIL);
MG Mud User88f12472016-06-24 23:31:02 +0200710 case 0: return RET_FAIL;
711 default: break;
712 }
Zesstraefad7be2018-11-06 23:18:30 +0100713 if (!MAY_READ(fullname))
714 return ERROR(NO_READ,fullname,RET_FAIL);
Zesstrac70bf582019-11-26 00:43:17 +0100715
716 // Bei case-insensitiver Suche das Pattern in Kleinschreibung wandeln
MG Mud User88f12472016-06-24 23:31:02 +0200717 if (flags&GREP_I)
718 rexpr=lower_case(rexpr);
Zesstrac70bf582019-11-26 00:43:17 +0100719
720 // Portionsweise das komplette File einlesen.
721 int maxlen = query_limits()[LIMIT_BYTE];
722 int ptr;
723 bytes read_buffer;
724 bytes data = b"";
725 while (sizeof(read_buffer = read_bytes(fullname, ptr, maxlen)))
MG Mud User88f12472016-06-24 23:31:02 +0200726 {
Zesstrac70bf582019-11-26 00:43:17 +0100727 data += read_buffer;
728 ptr += sizeof(read_buffer);
729 // die Schleifenbedingung erkennt zwar auch, wenn das File vollstaendig
730 // ist, aber wir koennen den Speicherzugriff auch einsparen und schauen,
731 // ob wir schon alles haben.
732 if (ptr >= filedata[FILESIZE])
733 break;
MG Mud User88f12472016-06-24 23:31:02 +0200734 }
Zesstrac70bf582019-11-26 00:43:17 +0100735 // In string konvertieren, wir gehen davon aus, das File ist UTF8-kodiert.
736 string text = to_text(data, "UTF-8");
737 string *lines = explode(text, "\n");
738 int count; // Anzahl Zeilen mit Treffern
739 <string|string*> result = ({}); // zutreffende Zeilen
740 int linecount = 1;
741 foreach(string line: lines)
MG Mud User88f12472016-06-24 23:31:02 +0200742 {
Zesstrac70bf582019-11-26 00:43:17 +0100743 string orig_line = line;
744 // Suche case-insensitive?
745 if (flags&GREP_I)
746 line = lower_case(line);
747 int match = regmatch(line, rexpr) != 0;
748 if (flags&GREP_V) match = !match; // Match ggf. invertieren
MG Mud User88f12472016-06-24 23:31:02 +0200749 if (match)
750 {
Zesstrac70bf582019-11-26 00:43:17 +0100751 // Ausgeben oder nicht?
752 if (!(flags&GREP_C))
MG Mud User88f12472016-06-24 23:31:02 +0200753 {
Zesstrac70bf582019-11-26 00:43:17 +0100754 // Mit Zeilennummer?
755 if (flags&GREP_N)
756 result+=({ sprintf("%4d %s", linecount, orig_line)});
MG Mud User88f12472016-06-24 23:31:02 +0200757 else
Zesstrac70bf582019-11-26 00:43:17 +0100758 result+=({orig_line});
MG Mud User88f12472016-06-24 23:31:02 +0200759 }
Zesstrac70bf582019-11-26 00:43:17 +0100760 ++count;
MG Mud User88f12472016-06-24 23:31:02 +0200761 }
Zesstrac70bf582019-11-26 00:43:17 +0100762 ++linecount;
MG Mud User88f12472016-06-24 23:31:02 +0200763 }
Zesstrac70bf582019-11-26 00:43:17 +0100764
MG Mud User88f12472016-06-24 23:31:02 +0200765 if (count)
766 {
Zesstrac70bf582019-11-26 00:43:17 +0100767 // Bei -h werden die Dateinamen unterdrueckt.
768 if (flags&GREP_H)
769 fullname="";
MG Mud User88f12472016-06-24 23:31:02 +0200770 else
Zesstrac70bf582019-11-26 00:43:17 +0100771 fullname=sprintf("%s ",fullname);
772
773 if (flags&GREP_C)
774 result=sprintf("%s%d passende Zeile%s.\n",fullname, count,
775 (count==1?"":"n"));
776 else
777 result = ( (sizeof(fullname) ? fullname + "\n" : "")
778 + implode(result,"\n") + "\n");
Zesstrac70bf582019-11-26 00:43:17 +0100779
Zesstra77c0bf22020-01-07 22:27:27 +0100780 // Ergebnis ausgeben in File oder an Magier
781 if (flags&GREP_F)
782 return write_file("/players/"+getuid()+"/grep.out",result);
783 write(result);
784 }
MG Mud User88f12472016-06-24 23:31:02 +0200785 return RET_OK;
786}
787
788static int _grep(string cmdline)
789{
790 string rexpr,mask;
791 mixed *args;
792 int flags;
793 cmdline=_unparsed_args();
794 args=parseargs(cmdline,&flags,GREP_OPTS,0);
795 if ((flags==-1)||!sizeof(args))
796 return USAGE("grep [-" GREP_OPTS
797 "] <regexp> <datei/verz> [<datei2> ... ] [<maske>]");
798 rexpr=args[0];
799 if (catch(regexp(({""}),rexpr))||!regexp(({""}),rexpr))
800 return printf("grep: Ungueltiger Suchausdruck: %s\n",rexpr) ,1;
Zesstracc3f2502018-11-14 23:46:47 +0100801 args=(sizeof(args)>1 ? args[1..] : ({}) );
MG Mud User88f12472016-06-24 23:31:02 +0200802 if (flags&GREP_M)
803 {
804 mask=args[<1];
Zesstracc3f2502018-11-14 23:46:47 +0100805 if (sizeof(args) > 2)
806 args = (sizeof(args) > 1 ? args[0..<2] : ({}) );
MG Mud User88f12472016-06-24 23:31:02 +0200807 }
808 if (!sizeof(args))
809 return USAGE("grep [-" GREP_OPTS
810 "] <regexp> <datei/verz> [<datei2> ... ] [<maske>]");
811 args=map(args,#'to_filename)-({0});
MG Mud User88f12472016-06-24 23:31:02 +0200812 args=file_list(args,MODE_GREP,(flags&GREP_R?1:0),"/",mask);
813 if (!sizeof(args))
814 return printf("Keine passenden Dateien gefunden.\n"),1;
815 if (flags&GREP_I) rexpr=lower_case(rexpr);
816 if (flags&GREP_F)
817 {
Zesstraefad7be2018-11-06 23:18:30 +0100818 if (file_size("/players/"+getuid()+"/grep.out")==FSIZE_DIR
819 || !MAY_WRITE("/players/"+getuid()+"/grep.out"))
MG Mud User88f12472016-06-24 23:31:02 +0200820 return printf("grep: Datei /players/%s/grep.out kann nicht "
Zesstraefad7be2018-11-06 23:18:30 +0100821 "geschrieben werden.\n",getuid()),1;
MG Mud User88f12472016-06-24 23:31:02 +0200822 else
823 write_file("/players/"+getuid()+"/grep.out",
824 "Ausgabe von \"grep " + _unparsed_args() + "\":\n");
825 }
826 asynchron(args,#'grep_file,rexpr,flags,0);
827 return 1;
828}