blob: 6929357548599901f23123f2dcca494a02d52faa [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 string term = QueryProp(P_TTY);
69 mapping vars = QueryProp(P_VARIABLES);
70 // 2nd value in templates is the additional amount of space the templates
71 // itself needs in addition to the value inserted later in place of %s.
72 lstemplates = (<lsformats> templates: m_allocate(3,2),
73 colormap:m_allocate(4,1) );
74 // 0-Key ist der default, wenn ein Platzhalter nicht gefunden wird. Wird
75 // z.B. fuer Ende-Tags benutzt.
76 lstemplates.colormap[0]=ANSI_NORMAL;
77 // Template fuer die Ausgabe und die Defaults fuer die Farbplatzhalter je
78 // nach Terminal einstellen.
MG Mud User88f12472016-06-24 23:31:02 +020079 switch(term=QueryProp(P_TTY))
80 {
81 case "vt100":
Zesstraa2bb5bf2021-04-20 17:45:22 +020082 lstemplates.templates[DIR] = "%^LS_DIR_START%^%s/%^LS_DIR_END%^";
83 lstemplates.templates[DIR, 1] = 1;
84 lstemplates.templates[OBJ] = "%^LS_OBJ_START%^%s%^LS_OBJ_END%^";
85 lstemplates.templates[VC] = "%^LS_VC_START%^%s%^LS_VC_END%^";
86 lstemplates.colormap["LS_DIR_START"]=ANSI_BOLD;
87 lstemplates.colormap["LS_OBJ_START"]=ANSI_INVERS;
88 lstemplates.colormap["LS_VC_START"]=ANSI_INVERS;
89 // Die *_END Platzhalter sind nicht explizit aufgefuehrt, solange sie
90 // nur ANSI_NORMAL normal waeren - in dem Fall benutzt terminal_colour()
91 // spaeter den 0-Key in colormap
92 break;
MG Mud User88f12472016-06-24 23:31:02 +020093 case "ansi":
Zesstraa2bb5bf2021-04-20 17:45:22 +020094 lstemplates.templates[DIR] = "%^LS_DIR_START%^%s%^LS_DIR_END%^";
95 lstemplates.templates[OBJ] = "%^LS_OBJ_START%^%s%^LS_OBJ_END%^";
96 lstemplates.templates[VC] = "%^LS_VC_START%^%s%^LS_VC_END%^";
97 lstemplates.colormap["LS_DIR_START"]=ANSI_BLUE+ANSI_BOLD;
98 lstemplates.colormap["LS_OBJ_START"]=ANSI_RED;
99 lstemplates.colormap["LS_VC_START"]=ANSI_PURPLE;
100 // Die *_END Platzhalter sind nicht explizit aufgefuehrt, solange sie
101 // nur ANSI_NORMAL normal waeren - in dem Fall benutzt terminal_colour()
102 // spaeter den 0-Key in colormap
MG Mud User88f12472016-06-24 23:31:02 +0200103 break;
Zesstraa2bb5bf2021-04-20 17:45:22 +0200104 // In diesen Faellen keine Farbcodeplatzhalter, nur zeichenbasierte
105 // Markierungen anhaengen.
MG Mud User88f12472016-06-24 23:31:02 +0200106 case "dumb":
MG Mud User88f12472016-06-24 23:31:02 +0200107 default:
Zesstraa2bb5bf2021-04-20 17:45:22 +0200108 m_add(lstemplates.templates, DIR, "%s/", 1);
109 m_add(lstemplates.templates, OBJ, "%s*", 1);
110 m_add(lstemplates.templates, VC, "%s*", 1);
111 break;
MG Mud User88f12472016-06-24 23:31:02 +0200112 }
Zesstraa2bb5bf2021-04-20 17:45:22 +0200113 // Die Defaults von oben koennen mit magierindividuellen Einstellungen
114 // ueberschrieben werden. Offensichtlich sind die nur wirksam fuer
115 // vt100/ansi und man koennte auch ANSI-Sequenzen einstellen, die ein vt100
116 // nicht versteht...
117 if(stringp(vars["LS_DIR"]))
118 lstemplates.colormap["LS_DIR_START"]=implode(map(
119 explode(vars["LS_DIR"],"+"), #'_get_color),"");
120 if(stringp(vars["LS_OBJ"]))
121 lstemplates.colormap["LS_OBJ_START"]=implode(map(
122 explode(vars["LS_OBJ"],"+"), #'_get_color),"");
123 if(stringp(vars["LS_VC"]))
124 lstemplates.colormap["LS_VC_START"]=implode(map(
125 explode(vars["LS_VC"],"+"), #'_get_color),"");
MG Mud User88f12472016-06-24 23:31:02 +0200126}
127
128
129private string _ls_output_short(mixed filedata,
130 int maxlen,int counter,int maxcount)
131{
132 string tmp;
133 tmp=filedata[BASENAME];
134 maxlen-=sizeof(tmp);
135 switch(filedata[FILESIZE])
136 {
Zesstraa2bb5bf2021-04-20 17:45:22 +0200137 case FSIZE_DIR: tmp=sprintf(lstemplates.templates[DIR],tmp);
138 maxlen-=lstemplates.templates[DIR,1]; break;
139 case FSIZE_NOFILE: tmp=sprintf(lstemplates.templates[VC],tmp);
140 maxlen-=lstemplates.templates[VC,1]; break;
MG Mud User88f12472016-06-24 23:31:02 +0200141 default: if (find_object(filedata[FULLNAME]))
142 {
Zesstraa2bb5bf2021-04-20 17:45:22 +0200143 maxlen-=lstemplates.templates[OBJ,1];
144 tmp=sprintf(lstemplates.templates[OBJ],tmp); break;
MG Mud User88f12472016-06-24 23:31:02 +0200145 }
146 }
147 if (!maxcount) return tmp+"\n";
Zesstraa2bb5bf2021-04-20 17:45:22 +0200148 return terminal_colour(sprintf("%-*s%s",(maxlen+sizeof(tmp)),tmp,
149 ((counter++)==maxcount?(counter=0,"\n"):" ")),
150 lstemplates.colormap);
MG Mud User88f12472016-06-24 23:31:02 +0200151}
152
153private int _ls_maxlen(mixed filedata,int flags,int maxlen)
154{
155 string base;
156 int size;
157 base=filedata[BASENAME];
158 if (!(flags&LS_A)&&(base[0]=='.')) return 0;
MG Mud User88f12472016-06-24 23:31:02 +0200159 maxlen=max(maxlen,sizeof(base));
MG Mud User88f12472016-06-24 23:31:02 +0200160 return 1;
161}
162
163private string _ls_output_long(mixed filedata, int flags,closure valid_read,
164 closure valid_write,closure creator_file)
165{
Zesstraefad7be2018-11-06 23:18:30 +0100166 string base=filedata[BASENAME];
167 if (!(sizeof(base)) || ( (!(flags&LS_A)) && (base[0]=='.')) )
MG Mud User88f12472016-06-24 23:31:02 +0200168 return 0;
Zesstraefad7be2018-11-06 23:18:30 +0100169 int size=filedata[FILESIZE];
170 string path=filedata[PATHNAME];
Zesstraf22b6a82018-11-07 22:39:55 +0100171 string full=filedata[FULLNAME];
Zesstraefad7be2018-11-06 23:18:30 +0100172 int dir=(size==FSIZE_DIR);
173 object ob=find_object(full);
174 int ftime=filedata[FILEDATE];
175 string date;
Bugfix59cb0e52019-09-06 15:17:29 +0200176 if ((time()-ftime)>31536000) // ein Jahr
Zesstraa2bb5bf2021-04-20 17:45:22 +0200177 date=strftime("%b %e %Y", ftime);
Zesstraefad7be2018-11-06 23:18:30 +0100178 else
179 date=strftime("%b %e %H:%M", ftime);
180
181 string creator="";
182 string group="";
MG Mud User88f12472016-06-24 23:31:02 +0200183 if (flags&LS_U)
184 {
Vanion50652322020-03-10 21:13:25 +0100185 creator=({string})call_other(master(),"creator_file", full);
MG Mud User88f12472016-06-24 23:31:02 +0200186 switch(creator)
187 {
188 case ROOTID: creator="root"; break;
Zesstraefad7be2018-11-06 23:18:30 +0100189 case BACKBONEID: creator="std"; break;
190 case MAILID: creator="mail"; break;
191 case NEWSID: creator="news"; break;
192 case NOBODY: creator="nobody"; break;
193 case POLIZEIID: creator="polizei"; break;
194 case DOCID: creator="doc"; break;
195 case GUILDID: creator="gilde"; break;
196 case ITEMID: creator="items"; break;
MG Mud User88f12472016-06-24 23:31:02 +0200197 default: if(!creator) creator="none"; break;
198 }
199 }
200 if (flags&LS_G)
201 {
Zesstraf22b6a82018-11-07 22:39:55 +0100202 string *tmp=explode(path, "/") - ({""});
MG Mud User88f12472016-06-24 23:31:02 +0200203 if (sizeof(tmp))
204 {
205 switch(tmp[0])
206 {
207 case WIZARDDIR: group="magier"; break;
Zesstraefad7be2018-11-06 23:18:30 +0100208 case NEWSDIR: group="news"; break;
209 case MAILDIR: group="mail"; break;
210 case FTPDIR: group="public"; break;
211 case PROJECTDIR: group="project"; break;
MG Mud User88f12472016-06-24 23:31:02 +0200212 case DOMAINDIR: if (sizeof(tmp)>1) { group=tmp[1]; break; }
213 default: group="mud"; break;
214 }
215 }
216 else group="mud";
217 }
Zesstraa2bb5bf2021-04-20 17:45:22 +0200218 if (dir) base=sprintf(lstemplates.templates[DIR],base);
MG Mud User88f12472016-06-24 23:31:02 +0200219 else
220 {
221 if (ob)
222 {
Zesstraefad7be2018-11-06 23:18:30 +0100223 if (size==FSIZE_NOFILE)
Zesstraa2bb5bf2021-04-20 17:45:22 +0200224 base=sprintf(lstemplates.templates[VC],base);
MG Mud User88f12472016-06-24 23:31:02 +0200225 else
Zesstraa2bb5bf2021-04-20 17:45:22 +0200226 base=sprintf(lstemplates.templates[OBJ],base);
MG Mud User88f12472016-06-24 23:31:02 +0200227 }
228 }
Zesstraa2bb5bf2021-04-20 17:45:22 +0200229 return terminal_colour(
230 sprintf(("%c%c%c%c %3d" + ((flags&LS_U) ? " %-24.24s" : "%-0.1s")
Zesstraefad7be2018-11-06 23:18:30 +0100231 +((flags&LS_G) ? " %-8.8s" : "%0.1s") + " %8s %s %s\n"),
232 (dir ? 'd' : '-'),
MG Mud User88f12472016-06-24 23:31:02 +0200233 (!funcall(valid_read,full,getuid(),
Zesstraefad7be2018-11-06 23:18:30 +0100234 "read_file",this_object()) ? '-' : 'r'),
MG Mud User88f12472016-06-24 23:31:02 +0200235 (!funcall(valid_write,full,getuid(),
Zesstraefad7be2018-11-06 23:18:30 +0100236 "write_file",this_object()) ? '-' : 'w'),
237 (ob ? 'x' : '-'),
238 (dir ? (sizeof((get_dir(full+"/*")||({}))-({".",".."}))) : 0),
239 creator, group,
240 (dir ? "-" : size==FSIZE_NOFILE ? "<vc>" : to_string(size)),
Zesstraa2bb5bf2021-04-20 17:45:22 +0200241 date, base),
242 lstemplates.colormap);
MG Mud User88f12472016-06-24 23:31:02 +0200243}
244
245
246static int _ls(string cmdline)
247{
248 int flags,cmp,i,arg_size,size;
249 int maxlen,counter,maxcount;
Arathorna8ae8662019-11-25 19:01:19 +0100250 mixed* args;
251 string output;
MG Mud User88f12472016-06-24 23:31:02 +0200252 mixed *tmp;
253 closure output_fun,v_read,v_write,creator,sort_fun;
254
255 cmdline=_unparsed_args()||"";
256 args=parseargs(cmdline,&flags,LS_OPTS,1);
257 if (flags==-1)
258 return USAGE("ls [-" LS_OPTS "] [<Verzeichnis>] [<Verzeichnis2> ..]");
259 SetColorstrings();
260 output="";
261 if (!sizeof(args)) args=({ QueryProp(P_CURRENTDIR) });
262 if (!sizeof(args=file_list(args,MODE_LSA,0,"/")))
263 return notify_fail("ls: Keine passenden Verzeichnisse gefunden.\n"), 0;
264// Files sortieren die erste
265 if (flags&LS_T) cmp=FILEDATE;
266 else if (flags&LS_S) cmp=FILESIZE;
267 else cmp=BASENAME; // =0 :-)
Arathorn06b52922018-11-26 20:47:10 +0100268
269 if ( !cmp && !(flags&LS_R) || cmp && (flags&LS_R) )
270 sort_fun = function int (mixed* a, mixed* b) {
271 return (a[cmp] > b[cmp]);
272 };
273 else
274 sort_fun = function int (mixed* a, mixed* b) {
275 return (a[cmp] < b[cmp]);
276 };
MG Mud User88f12472016-06-24 23:31:02 +0200277 args=sort_array(args,sort_fun);
278// Ausgabeformat bestimmen
279 if (flags&LS_L)
280 {
281 v_read=VALID_READ_CL;
282 v_write=VALID_WRITE_CL;
283 creator=CREATOR_CL;
284 }
285 arg_size=sizeof(args);
286 if (arg_size>1||(arg_size&&args[0][FILESIZE]>=0))
287 {
288 if (flags&LS_L)
289 tmp=map(args,#'_ls_output_long,flags,v_read,
290 v_write,creator)-({0});
291 else
292 {
293 counter=0;maxlen=0;
294 tmp=filter(args,#'_ls_maxlen,flags,&maxlen);
295 if (maxlen>76) maxlen=76;
296 maxcount=(78/(maxlen+2))-1;
297 tmp=map(tmp,#'_ls_output_short,maxlen,&counter,maxcount);
298 }
299 output=sprintf("\n%d Dateien/Unterverzeichnisse:\n%s\n",
300 sizeof(tmp),implode(tmp,""));
301 }
302 for(i=0;i<arg_size;i++)
303 {
304 tmp=({});
305 size=args[i][FILESIZE];
Zesstraefad7be2018-11-06 23:18:30 +0100306 if (size==FSIZE_DIR)
MG Mud User88f12472016-06-24 23:31:02 +0200307 {
308 tmp=file_list(({args[i][FULLNAME]+"/*"}),MODE_LSB,0,"/");
309 tmp=sort_array(tmp,sort_fun);
310 if (flags&LS_L)
311 tmp=map(tmp,#'_ls_output_long,flags,v_read,
312 v_write,creator)-({0});
313 else
314 {
315 counter=0;maxlen=0;
316 tmp=filter(tmp,#'_ls_maxlen,flags,&maxlen);
317 maxcount=(78/(maxlen+2));
318 if (maxcount) maxcount--;
319 tmp=map(tmp,#'_ls_output_short,maxlen,&counter,maxcount);
320 }
321 if (sizeof(tmp))
322 {
323 output+=sprintf("\n%s: Verzeichnis, %d Dateien/Unterverzeichnisse:\n",
324 args[i][FULLNAME],sizeof(tmp));
325 output+=(implode(tmp,"")+((string)(tmp[<1][<1..<1])=="\n"?"":"\n"));
326 }
327 else
328 {
329 output+=sprintf("\n%s: Leeres Verzeichnis.\n",args[i][FULLNAME]);
330 }
331 }
332 }
333 More(output);
334 return 1;
335}
336
337// ########
338//############################### MORE ###################################
339// ########
340//
341// _more_file: Mehrere Files hintereinander ausgeben
342//
343
344private void _more_file(string *arg)
345{
346 if (!sizeof(arg)) return;
347 printf("more: Naechste Datei: %s\n",arg[0]);
Zesstra3de3a032018-11-08 19:17:03 +0100348 More(arg[0],1,#'_more_file,
349 (sizeof(arg)>1 ? ({ arg[1..]}) : ({})) );
MG Mud User88f12472016-06-24 23:31:02 +0200350 return;
351}
352
353
354private mixed _size_filter(mixed *arg)
355{
356 if (arg[FILESIZE]>0) return arg[FULLNAME];
357 if (arg[FILESIZE]==0)
358 {
359 printf("%s: %s: Leere Datei.\n",query_verb()||"more",arg[FULLNAME]);
360 return 0;
361 }
Zesstraefad7be2018-11-06 23:18:30 +0100362 if (arg[FILESIZE]==FSIZE_DIR)
MG Mud User88f12472016-06-24 23:31:02 +0200363 printf("%s: %s ist ein Verzeichnis.\n",query_verb()||"more",arg[FULLNAME]);
364 else
365 printf("%s: %s: Datei existiert nicht.\n", query_verb()||"more",
366 arg[FULLNAME]);
367 return 0;
368}
369
370
371//
372// _more: Dateien anzeigen
373// cmdline: Kommandozeile
374//
375
376static int _more(string cmdline)
377{
378 mixed *args;
379 int flags;
380 cmdline=_unparsed_args();
381 args=parseargs(cmdline,&flags,"",1);
382 if (flags==-1||!sizeof(args)) return USAGE("more <datei> [<datei2>..]");
383 args=file_list(args,MODE_MORE,0,"/");
384 if (!sizeof(args))
385 return printf("more: %s: Keine passende Datei gefunden.\n",cmdline),1;
386 args=map(args,#'_size_filter)-({0});
387 if (sizeof(args)) _more_file(args);
388 return 1;
389}
390
391// ###################
392//########################## HEAD, TAIL, CAT #############################
393// ###################
394
395static int _cat(string cmdline)
396{
397 mixed *args;
398 int flags;
399 cmdline=_unparsed_args();
400 args=parseargs(cmdline,&flags,"",1);
401 if(flags==-1||!sizeof(args))
402 return USAGE(query_verb()+" <dateiname> [<datei2>..]");
403 args=file_list(args,MODE_CAT,0,"/");
404 if (!sizeof(args))
405 return printf("%s: %s: Keine passende Datei gefunden.\n",query_verb(),
406 cmdline),1;
407 args=map(args,#'_size_filter)-({0});
408 if (!sizeof(args)) return 1;
409 while(sizeof(args))
410 {
411 switch(query_verb())
412 {
413 case "cat":
414 if (!cat(args[0]))
415 printf("cat: %s konnte nicht angezeigt werden.\n",args[0]);
416 break;
417 case "head":
418 if (!cat(args[0],0,10))
419 printf("head: %s konnte nicht angezeigt werden.\n",args[0]);
420 break;
421 case "tail": tail(args[0]); break;
422 }
Zesstra3de3a032018-11-08 19:17:03 +0100423 if (sizeof(args) > 1)
424 args=args[1..];
425 else
426 break;
MG Mud User88f12472016-06-24 23:31:02 +0200427 }
428 return 1;
429}
430
431// #######
432//############################### MAN ###################################
433// #######
434
Arathorn99c40582020-08-28 19:46:23 +0200435/* Sortiert ein Array paarweise, wobei von jedem Wertepaar der erste Wert
436 fuer die Sortierung herangezogen wird. */
437private string* SortInPairs(string* arr) {
438 if (!sizeof(arr))
439 return arr;
440
441 string** helper = ({});
442
443 /* In Sub-Arrays zerlegen, die jeweils ein Wertepaart enthalten:
444 ({s0, s1, s2, s3}) => ({({s0,s1}), ({s2,s3})}) */
445 int listsize = sizeof(arr);
446 for(int i ; i<listsize ; i+=2) {
447 helper += ({ arr[i..i+1] });
448 }
449
450 // Nach dem ersten Element jedes Sub-Arrays sortieren.
451 helper = sort_array(helper, function int (string* h1, string* h2) {
452 return lower_case(h1[0]) > lower_case(h2[0]);
453 });
454
455 // Eingabe-Array loeschen und aus den sortierten Wertepaaren neu aufbauen.
456 arr = ({});
457 foreach(string* h : helper) {
458 arr += h;
459 }
460
461 return arr;
462}
463
MG Mud User88f12472016-06-24 23:31:02 +0200464static int _man(string cmdline)
465{
MG Mud User88f12472016-06-24 23:31:02 +0200466 int i, flags;
Arathorndb201cc2020-08-28 17:02:38 +0200467 string *input;
MG Mud User88f12472016-06-24 23:31:02 +0200468
Arathorn1facd5f2020-08-28 16:41:17 +0200469 string* args = parseargs(_unparsed_args(), &flags, MAN_OPTS, 0);
470
MG Mud User88f12472016-06-24 23:31:02 +0200471 if (flags==-1 ||
472 (sizeof(args)!=1 &&
473 (sizeof(args)<2 || sizeof(args[1])>1 || !(i=to_int(args[1])))))
474 return USAGE("man [-" MAN_OPTS "] <hilfeseite>");
MG Mud User88f12472016-06-24 23:31:02 +0200475
Arathorn72c7e542020-08-28 16:50:28 +0200476 input = explode(args[0], "/");
477
Arathorn815f2742020-08-28 17:29:04 +0200478 /* Wenn das Ergebnis einer vorherigen Suche noch vorliegt und die aktuelle
479 Eingabe als einziges eine einstellige Zahl enthaelt, und diese dann in
480 dem alten Suchergebnis enthalten ist, wird die Eingabe durch das alte
481 Ergebnis ersetzt. <i> wird in dem Fall geloescht.
482 Wenn kein altes Ergebnis gefunden wurde, enthaelt <i> die eingegebene
483 Nummer. */
Arathorn72c7e542020-08-28 16:50:28 +0200484 if (oldman_result && sizeof(input)==1 && sizeof(args)==1 &&
485 sizeof(input[0])==1 && (i=to_int(input[0])) &&
Arathornd0a1e662020-08-28 17:01:24 +0200486 member(oldman_result,i))
487 {
Arathorn72c7e542020-08-28 16:50:28 +0200488 input = ({oldman_result[i,0], oldman_result[i,1]});
Arathornd0a1e662020-08-28 17:01:24 +0200489 i = 0;
MG Mud User88f12472016-06-24 23:31:02 +0200490 }
Arathorn815f2742020-08-28 17:29:04 +0200491 /* Ansonsten wenn weder -m, noch -r gesetzt sind und es eine Manpage gibt,
492 die genau der Eingabe entspricht, wird diese verwendet. */
Arathorn72c7e542020-08-28 16:50:28 +0200493 else if (!(flags&(MAN_M|MAN_R)) && sizeof(input)>1)
MG Mud User88f12472016-06-24 23:31:02 +0200494 {
Arathornd0a1e662020-08-28 17:01:24 +0200495 if (file_size(MAND_DOCDIR+args[0]) >= 0)
Arathorn72c7e542020-08-28 16:50:28 +0200496 input = ({input[<1], args[0]});
MG Mud User88f12472016-06-24 23:31:02 +0200497 else
Arathorn72c7e542020-08-28 16:50:28 +0200498 input = ({});
MG Mud User88f12472016-06-24 23:31:02 +0200499 }
500 else
501 {
Arathorn815f2742020-08-28 17:29:04 +0200502 /* Soll eine Regexp-Suche durchgefuehrt werden? Dann erstmal den Ausdruck
503 auf Gueltigkeit pruefen. */
MG Mud User88f12472016-06-24 23:31:02 +0200504 if (flags&MAN_R)
505 {
Arathornd0a1e662020-08-28 17:01:24 +0200506 flags &= (~MAN_M);
507 if (catch(regexp(({""}), args[0])) || !regexp(({""}), args[0]))
508 {
509 printf("man: Ungueltiger Ausdruck in Maske.\n");
510 return 1;
511 }
MG Mud User88f12472016-06-24 23:31:02 +0200512 }
Arathorn815f2742020-08-28 17:29:04 +0200513 /* Die Ausgabe von locate() liefert ein String-Array, das abwechselnd den
514 Dateinamen der gefundenen Manpage und den vollen Pfad dieser Manpage
515 unterhalb von /doc enthaelt. Beispielsweise ({"Defend","lfun/Defend"})
516 */
Arathorn72c7e542020-08-28 16:50:28 +0200517 input = ({string *})MAND->locate(args[0], flags&(MAN_M|MAN_R));
Arathorn84dd5f22020-08-28 17:24:44 +0200518 // Sortierung case-insensitive, ggf. vorhandene Pfade dabei ignorieren
519 // Wird fuer die spaetere Ausgabe der Liste benoetigt.
Arathorn99c40582020-08-28 19:46:23 +0200520 input = SortInPairs(input);
MG Mud User88f12472016-06-24 23:31:02 +0200521 }
522
Arathorn815f2742020-08-28 17:29:04 +0200523 /* Alte Such-Treffer werden entsorgt. */
Arathornd0a1e662020-08-28 17:01:24 +0200524 oldman_result = 0;
525
Arathorn815f2742020-08-28 17:29:04 +0200526 /* <i> kann maximal eine einstellige Zahl sein, 1-9, wenn eine solche als
527 einziges Argument eingegeben wurde und kein passendes Ergebnis in einer
528 vorigen Suchanfrage gefunden wurde.
529
530 <input> kann nur genau dann mehr als 2 Elemente haben, wenn das Ergebnis
531 des Aufrufs MAND->locate() drinsteht. Und nur dann muss ueberhaupt ein
532 hoeheres Wertepaar rausgeschnitten werden, denn wenn <input> bis zu 2
533 Elemente hat, kann damit direkt weitergearbeitet werden.
534
535 Beispiel: bei einer Eingabe von "4" wird hier aus <input> das 7. und
536 8. Element entnommen (Indizes [6..7]). */
Arathorn131bb742020-08-28 17:05:02 +0200537 if(i && sizeof(input)>2 && sizeof(input) >= i*2)
538 input = input[(i*2-2)..(i*2-1)];
Arathornd0a1e662020-08-28 17:01:24 +0200539
Arathorn72c7e542020-08-28 16:50:28 +0200540 switch (sizeof(input))
MG Mud User88f12472016-06-24 23:31:02 +0200541 {
Arathorn815f2742020-08-28 17:29:04 +0200542 /* <input> leer, nichts brauchbares gefunden. */
MG Mud User88f12472016-06-24 23:31:02 +0200543 case 0:
Arathornd0a1e662020-08-28 17:01:24 +0200544 printf("Keine Hilfeseite gefunden fuer '%s'.\n", args[0]);
MG Mud User88f12472016-06-24 23:31:02 +0200545 break;
Arathornd0a1e662020-08-28 17:01:24 +0200546
Arathorn815f2742020-08-28 17:29:04 +0200547 /* Genau 2 Elemente enthalten? Dann kann das direkt ausgegeben werden. */
MG Mud User88f12472016-06-24 23:31:02 +0200548 case 2:
549 /*
550 if (flags&MAN_I)
551 {
552 // BRANCH TO MANUALD
553 return 1;
554 }
555 */
Arathorn72c7e542020-08-28 16:50:28 +0200556 printf("Folgende Hilfeseite wurde gefunden: %s\n", input[1]);
557 More(MAND_DOCDIR+input[1], 1);
MG Mud User88f12472016-06-24 23:31:02 +0200558 return 1;
Arathorn815f2742020-08-28 17:29:04 +0200559
560 /* Alles andere: */
MG Mud User88f12472016-06-24 23:31:02 +0200561 default:
Arathorn131bb742020-08-28 17:05:02 +0200562 i = sizeof(input)/2;
Arathorndb201cc2020-08-28 17:02:38 +0200563 string* output = allocate(i);
Arathorn815f2742020-08-28 17:29:04 +0200564
565 // Inhalt: ([ int nummer : string manpage; string manpage_pfad ])
Arathornd0a1e662020-08-28 17:01:24 +0200566 oldman_result = m_allocate(i, 2);
Arathorn815f2742020-08-28 17:29:04 +0200567
Arathornd0a1e662020-08-28 17:01:24 +0200568 while (i)
MG Mud User88f12472016-06-24 23:31:02 +0200569 {
Arathorn131bb742020-08-28 17:05:02 +0200570 output[i-1] = input[i*2-2];
Arathorn1719b252020-08-28 19:44:32 +0200571 m_add(oldman_result, i, input[i*2-2], input[i*2-1]);
MG Mud User88f12472016-06-24 23:31:02 +0200572 i--;
573 }
Arathorndb5d16d2020-05-11 01:23:18 +0200574
Arathorndb5d16d2020-05-11 01:23:18 +0200575 // Numerierung ergaenzen
Arathorndb201cc2020-08-28 17:02:38 +0200576 foreach(int j : sizeof(output))
Arathornd0a1e662020-08-28 17:01:24 +0200577 {
Arathorndb201cc2020-08-28 17:02:38 +0200578 output[j] = sprintf("%d: %s", j+1, output[j]);
Arathorndb5d16d2020-05-11 01:23:18 +0200579 }
580
581 int tty_cols = QueryProp(P_TTY_COLS)-2;
582 string list = "Es wurden die folgenden, potentiell passenden Seiten "
583 "gefunden:\n";
584
585 // Wer keine Grafik sehen will, bekommt eine andere Anzeige.
Arathornd0a1e662020-08-28 17:01:24 +0200586 if (QueryProp(P_NO_ASCII_ART))
587 {
Arathorndb5d16d2020-05-11 01:23:18 +0200588 // @ als geschuetztes Leerzeichen verwenden, um einen Umbruch
589 // nach den Nummern zu verhindern.
Arathorndb201cc2020-08-28 17:02:38 +0200590 output = map(output, #'regreplace, ": ", ":@", 1);
591 list += break_string(implode(output, " "), tty_cols);
Arathorndb5d16d2020-05-11 01:23:18 +0200592 list = regreplace(list, ":@", ": ", 1);
593 }
Arathornd0a1e662020-08-28 17:01:24 +0200594 else
595 {
Arathorndb5d16d2020-05-11 01:23:18 +0200596 // Anzahl Spalten ausrechnen: Terminalbreite / Laenge des laengsten
Arathorndb201cc2020-08-28 17:02:38 +0200597 // Elements in <output>. Kann der Spaltenmodus von sprintf() an sich
Arathorndb5d16d2020-05-11 01:23:18 +0200598 // selbst, das liefert aber nicht immer so guenstige Ergebnisse.
Arathorndb201cc2020-08-28 17:02:38 +0200599 int maxwidth = max(map(output, #'sizeof));
Arathorndb5d16d2020-05-11 01:23:18 +0200600 int tablecols = tty_cols/maxwidth;
601 list += "-"*tty_cols+"\n"+
Arathorndb201cc2020-08-28 17:02:38 +0200602 sprintf("%#-*.*s\n", tty_cols, tablecols, implode(output,"\n"))+
Arathorndb5d16d2020-05-11 01:23:18 +0200603 "-"*tty_cols+"\n";
604 }
605 printf(list);
MG Mud User88f12472016-06-24 23:31:02 +0200606 break;
607 }
608 return 1;
609}
610
611// ########
612//############################### RMAN ##################################
613// ########
614
615static int _rman(string str)
616{
617 int flags;
618 string *args;
619
620 str=_unparsed_args();
621 args=parseargs(str,&flags,"",0);
622 if (flags==-1||sizeof(args)!=2)
623 return USAGE("rman <hilfeseite> <mudname>");
624 write("man: " +
Vanion50652322020-03-10 21:13:25 +0100625 ({string})call_other(UDP_CMD_DIR+"man","send_request",args[1],args[0]));
MG Mud User88f12472016-06-24 23:31:02 +0200626 return 1;
627}
628
629
630// #############
631//############################# SHOWPROPS ###############################
632// #############
633
634//
635// _showprops: Zeigt Properties zu einem Objekt an
636//
637
638static int _showprops(string str)
639{
640 int i;
641 string *list, ausgabe;
642
643 if (str) {
644 if (str[0]!='/') str="/"+str;
645 if (str[0..4]!="/std/")
646 {
647 printf("showprops: Es koennen nur Properties von Objekten "
648 "aus /std/ angezeigt werden.\n");
649 return 1;
650 }
651 if (str[<1]=='.') str+="c";
652 if (str[<2..<1]!=".c") str+=".c";
653 if (file_size(str)<0)
654 {
655 printf("showprops: %s: Es gibt kein Objekt diesen Namens.\n",str[0..<3]);
656 return 1;
657 }
Zesstraefad7be2018-11-06 23:18:30 +0100658 if (catch(load_object(str)))
MG Mud User88f12472016-06-24 23:31:02 +0200659 {
660 printf("showprops: %s: Datei konnte nicht geladen werden.\n",str);
661 return 1;
662 }
663 }
664 if (!str || !find_object(str)) {
665 notify_fail("Welche Properties moechtest Du sehen?"
666 " Bsp.: <showprops /std/npc>\n");
667 return 0;
668 }
669 list=inherit_list(find_object(str));
MG Mud User88f12472016-06-24 23:31:02 +0200670 list=map(list,(: return $1[5..<2]+"h"; :));
671 list+=map(list,(: return explode($1,"/")[<1]; :));
672 list=map(m_indices(mkmapping(list)),(: return "/sys/"+$1; :));
673 list=filter(list,(: return file_size($1)>0; :));
MG Mud User88f12472016-06-24 23:31:02 +0200674 list=sort_array(list, #'<);
675 ausgabe="";
676 for (i=sizeof(list);i--;)
677 {
MG Mud User88f12472016-06-24 23:31:02 +0200678 str=implode(filter(explode(read_file(list[i]),"\n"),
679 (: return $1[0..9]=="#define P_";:)),"\n");
MG Mud User88f12472016-06-24 23:31:02 +0200680 if (str!="") ausgabe+=sprintf("%s\n%s\n\n", list[i], str);
681 }
682 if (ausgabe!="")
683 More(ausgabe);
684 else
685 printf("Keine Property-Definitionen zu diesem Objekt gefunden!\n");
686 return 1;
687}
688
689// ########
690//############################### GREP ###################################
691// ########
692
MG Mud User88f12472016-06-24 23:31:02 +0200693//
694// grep_file: Datei greppen
695// rexpr: Regular Expression
696// flags: Flags
697//
698
699private int grep_file(mixed filedata, string rexpr, int flags)
700{
Zesstrac70bf582019-11-26 00:43:17 +0100701 string fullname=filedata[FULLNAME];
MG Mud User88f12472016-06-24 23:31:02 +0200702 if ((flags&GREP_F)&&fullname=="/players/"+getuid()+"/grep.out")
Zesstraefad7be2018-11-06 23:18:30 +0100703 {
704 write_file("/players/"+getuid()+"/grep.out",
705 "Uebergehe grep.out ...\n");
706 return RET_FAIL;
MG Mud User88f12472016-06-24 23:31:02 +0200707 }
708 switch(filedata[FILESIZE])
709 {
Zesstraefad7be2018-11-06 23:18:30 +0100710 case FSIZE_DIR: return RET_FAIL;
711 case FSIZE_NOFILE: return ERROR(DOESNT_EXIST,fullname,RET_FAIL);
MG Mud User88f12472016-06-24 23:31:02 +0200712 case 0: return RET_FAIL;
713 default: break;
714 }
Zesstraefad7be2018-11-06 23:18:30 +0100715 if (!MAY_READ(fullname))
716 return ERROR(NO_READ,fullname,RET_FAIL);
Zesstrac70bf582019-11-26 00:43:17 +0100717
718 // Bei case-insensitiver Suche das Pattern in Kleinschreibung wandeln
MG Mud User88f12472016-06-24 23:31:02 +0200719 if (flags&GREP_I)
720 rexpr=lower_case(rexpr);
Zesstrac70bf582019-11-26 00:43:17 +0100721
722 // Portionsweise das komplette File einlesen.
723 int maxlen = query_limits()[LIMIT_BYTE];
724 int ptr;
725 bytes read_buffer;
726 bytes data = b"";
727 while (sizeof(read_buffer = read_bytes(fullname, ptr, maxlen)))
MG Mud User88f12472016-06-24 23:31:02 +0200728 {
Zesstrac70bf582019-11-26 00:43:17 +0100729 data += read_buffer;
730 ptr += sizeof(read_buffer);
731 // die Schleifenbedingung erkennt zwar auch, wenn das File vollstaendig
732 // ist, aber wir koennen den Speicherzugriff auch einsparen und schauen,
733 // ob wir schon alles haben.
734 if (ptr >= filedata[FILESIZE])
735 break;
MG Mud User88f12472016-06-24 23:31:02 +0200736 }
Zesstrac70bf582019-11-26 00:43:17 +0100737 // In string konvertieren, wir gehen davon aus, das File ist UTF8-kodiert.
738 string text = to_text(data, "UTF-8");
739 string *lines = explode(text, "\n");
740 int count; // Anzahl Zeilen mit Treffern
741 <string|string*> result = ({}); // zutreffende Zeilen
742 int linecount = 1;
743 foreach(string line: lines)
MG Mud User88f12472016-06-24 23:31:02 +0200744 {
Zesstrac70bf582019-11-26 00:43:17 +0100745 string orig_line = line;
746 // Suche case-insensitive?
747 if (flags&GREP_I)
748 line = lower_case(line);
749 int match = regmatch(line, rexpr) != 0;
750 if (flags&GREP_V) match = !match; // Match ggf. invertieren
MG Mud User88f12472016-06-24 23:31:02 +0200751 if (match)
752 {
Zesstrac70bf582019-11-26 00:43:17 +0100753 // Ausgeben oder nicht?
754 if (!(flags&GREP_C))
MG Mud User88f12472016-06-24 23:31:02 +0200755 {
Zesstrac70bf582019-11-26 00:43:17 +0100756 // Mit Zeilennummer?
757 if (flags&GREP_N)
758 result+=({ sprintf("%4d %s", linecount, orig_line)});
MG Mud User88f12472016-06-24 23:31:02 +0200759 else
Zesstrac70bf582019-11-26 00:43:17 +0100760 result+=({orig_line});
MG Mud User88f12472016-06-24 23:31:02 +0200761 }
Zesstrac70bf582019-11-26 00:43:17 +0100762 ++count;
MG Mud User88f12472016-06-24 23:31:02 +0200763 }
Zesstrac70bf582019-11-26 00:43:17 +0100764 ++linecount;
MG Mud User88f12472016-06-24 23:31:02 +0200765 }
Zesstrac70bf582019-11-26 00:43:17 +0100766
MG Mud User88f12472016-06-24 23:31:02 +0200767 if (count)
768 {
Zesstrac70bf582019-11-26 00:43:17 +0100769 // Bei -h werden die Dateinamen unterdrueckt.
770 if (flags&GREP_H)
771 fullname="";
MG Mud User88f12472016-06-24 23:31:02 +0200772 else
Zesstrac70bf582019-11-26 00:43:17 +0100773 fullname=sprintf("%s ",fullname);
774
775 if (flags&GREP_C)
776 result=sprintf("%s%d passende Zeile%s.\n",fullname, count,
777 (count==1?"":"n"));
778 else
779 result = ( (sizeof(fullname) ? fullname + "\n" : "")
780 + implode(result,"\n") + "\n");
Zesstrac70bf582019-11-26 00:43:17 +0100781
Zesstra77c0bf22020-01-07 22:27:27 +0100782 // Ergebnis ausgeben in File oder an Magier
783 if (flags&GREP_F)
784 return write_file("/players/"+getuid()+"/grep.out",result);
785 write(result);
786 }
MG Mud User88f12472016-06-24 23:31:02 +0200787 return RET_OK;
788}
789
790static int _grep(string cmdline)
791{
792 string rexpr,mask;
793 mixed *args;
794 int flags;
795 cmdline=_unparsed_args();
796 args=parseargs(cmdline,&flags,GREP_OPTS,0);
797 if ((flags==-1)||!sizeof(args))
798 return USAGE("grep [-" GREP_OPTS
799 "] <regexp> <datei/verz> [<datei2> ... ] [<maske>]");
800 rexpr=args[0];
801 if (catch(regexp(({""}),rexpr))||!regexp(({""}),rexpr))
802 return printf("grep: Ungueltiger Suchausdruck: %s\n",rexpr) ,1;
Zesstracc3f2502018-11-14 23:46:47 +0100803 args=(sizeof(args)>1 ? args[1..] : ({}) );
MG Mud User88f12472016-06-24 23:31:02 +0200804 if (flags&GREP_M)
805 {
806 mask=args[<1];
Zesstracc3f2502018-11-14 23:46:47 +0100807 if (sizeof(args) > 2)
808 args = (sizeof(args) > 1 ? args[0..<2] : ({}) );
MG Mud User88f12472016-06-24 23:31:02 +0200809 }
810 if (!sizeof(args))
811 return USAGE("grep [-" GREP_OPTS
812 "] <regexp> <datei/verz> [<datei2> ... ] [<maske>]");
813 args=map(args,#'to_filename)-({0});
MG Mud User88f12472016-06-24 23:31:02 +0200814 args=file_list(args,MODE_GREP,(flags&GREP_R?1:0),"/",mask);
815 if (!sizeof(args))
816 return printf("Keine passenden Dateien gefunden.\n"),1;
817 if (flags&GREP_I) rexpr=lower_case(rexpr);
818 if (flags&GREP_F)
819 {
Zesstraefad7be2018-11-06 23:18:30 +0100820 if (file_size("/players/"+getuid()+"/grep.out")==FSIZE_DIR
821 || !MAY_WRITE("/players/"+getuid()+"/grep.out"))
MG Mud User88f12472016-06-24 23:31:02 +0200822 return printf("grep: Datei /players/%s/grep.out kann nicht "
Zesstraefad7be2018-11-06 23:18:30 +0100823 "geschrieben werden.\n",getuid()),1;
MG Mud User88f12472016-06-24 23:31:02 +0200824 else
825 write_file("/players/"+getuid()+"/grep.out",
826 "Ausgabe von \"grep " + _unparsed_args() + "\":\n");
827 }
828 asynchron(args,#'grep_file,rexpr,flags,0);
829 return 1;
830}