blob: a38210aa3335991a5bb4fc60d950b0ae9cc13b88 [file] [log] [blame]
MG Mud User88f12472016-06-24 23:31:02 +02001// $Id: parsing.c 9142 2015-02-04 22:17:29Z Zesstra $
Zesstra0f6a3022018-11-07 00:44:32 +01002#pragma strict_types, rtt_checks
Zesstra0d64cca2020-03-09 21:03:56 +01003#pragma range_check
MG Mud User88f12472016-06-24 23:31:02 +02004#pragma no_clone
MG Mud User88f12472016-06-24 23:31:02 +02005
Zesstra07210ee2018-11-08 20:57:14 +01006protected functions virtual inherit "/std/util/path";
7
MG Mud User88f12472016-06-24 23:31:02 +02008#include <files.h>
9#include <wizlevels.h>
10#include <logging.h>
11#include <regexp.h>
Zesstraf22b6a82018-11-07 22:39:55 +010012#include <defines.h>
MG Mud User88f12472016-06-24 23:31:02 +020013#define NEED_PROTOTYPES
14#include <magier.h>
15#include <thing/properties.h>
16#include <player.h>
17
18//
19// glob2regexp: Argument von glob in Regexp umwandeln
20// str: Argument (als Referenz)
21//
22
23static string glob2regexp(string str)
24{
25 str=regreplace(str,"([\\.\\^\\$\\[\\]\\(\\)])","\\\\\\1",RE_TRADITIONAL|1);
26 str=regreplace(str,"\\*",".*",RE_TRADITIONAL|1);
27 return sprintf("^%s$",regreplace(str,"?",".",RE_TRADITIONAL|1));
28}
29
30//
31// to_filename: Argument in Dateinamen umwandeln
32// str: Argument
33// Rueckgabe: Dateiname
34//
35
Vanion50652322020-03-10 21:13:25 +010036static string to_filename(string str)
MG Mud User88f12472016-06-24 23:31:02 +020037{
38 string *tmp,p,newfn;
39 int i;
40// Testen ob .. in einem Filenamenabschnitt, falls Version <3.2.5
41 tmp=explode(str,"/");
42// Testen auf Pfadvariable
43 if (sizeof(tmp[0]) && tmp[0][0]=='$'
44 && m_contains(&p,QueryProp(P_VARIABLES),tmp[0][1..]))
45 tmp[0]=p;
46// Pfad absolut machen (Hat danach noch Wildcards drinnen) oder auch nicht
Zesstra07210ee2018-11-08 20:57:14 +010047 return normalize_path(implode(tmp,"/"), getuid(), 1);
MG Mud User88f12472016-06-24 23:31:02 +020048}
49
50
51//
52// _parseargs(): Kommandozeilenabschnitt parsen
53// str: Kommandozeilenabschnitt
54// line: Array von geparsten Kommandozeilenabschnitten (Referenz)
55// flags: Als Referenz uebergebener Flag-Wert
56// opts: Erlaubte Flags
57// build_fn: Filenamen aendern
58//
59
60private void _parseargs(string str, string *line,int flags,string opts,
61 int build_fn )
62{
63
64// Strings in "" erhalten
65 if(str[0] == "\""[0])
66 {
67 line += ({ str[1..<2] });
68 return;
69 }
70// Flags parsen
71 if(str[0] == '-')
72 {
73 int i,j;
74 i=sizeof(str);
75 while(i--)
76 if (str[i]!='-')
77 {
78 if((j = member(opts, str[i])) != -1)
79 flags |= (1 << j);
80 else
81 {
82 flags=-1;
83 printf("Das Flag '-%c' wird von dem Befehl '%s' nicht "
84 "unterstuetzt.\n",str[i],query_verb()||"");
85 }
86 }
87 return;
88 }
89 if (build_fn)
90 {
Vanion50652322020-03-10 21:13:25 +010091 if (str=to_filename(str)) line+=({ str });
MG Mud User88f12472016-06-24 23:31:02 +020092 }
93 else
94 line+= ({ str });
95}
96
97
98//
99// parseargs() - zerlegt Kommandozeile in ein Array:
100// cmdline: Kommandozeile
101// flags: Als Referenz uebergebener leerer Integerwert. Enthaelt danach
102// die Flags
103// opts: String mit erlaubten Optionen
104// build_fn: Filenamen umbauen?
105// Rueckgabe: Array der Kommandozeilenargumente (ausser Flags)
106//
107
108static string *parseargs(string cmdline,int flags, string opts,int build_fn)
109{
110 int i;
111 string *line;
112 line=({});
113 if (!sizeof(cmdline)) return ({});
114 map(regexplode(cmdline,"[\"][^\"]*[\"]| ", RE_TRADITIONAL)-({" ", ""}),
115 #'_parseargs, &line, &flags,opts, build_fn);
116 return line - ({""});
117}
118
119//
120// _vc_map: VC-Objektnamen zu Arrays umwandeln ({ name, -1, program_time() })
121//
122
123private int *_vc_map(object ob,mixed *list)
124{
125 list+=({ explode(object_name(ob),"/")[<1],-1,program_time(ob) });
126 return 0;
127}
128
129
130//
131// _get_files: Filedaten beim Rekursiven kopieren erstellen
132// dirname: Bearbeitetes Verzeichnis
133// mask: Maske, der Dateinamen genuegen muessen
134// mode: Welche Daten werden benoetigt? (siehe magier.h)
135// dest: In welches Verzeichnis soll kopiert werden?
136// Rueckgabe: Alist mit den Dateiinformationen
137//
138
Zesstra0f6a3022018-11-07 00:44:32 +0100139private varargs mixed *_get_files(string dirname,string mask,int mode,
140 string dest)
MG Mud User88f12472016-06-24 23:31:02 +0200141{
MG Mud User88f12472016-06-24 23:31:02 +0200142 //DEBUG("_GF: DIRNAME " + dirname);
Zesstra0f6a3022018-11-07 00:44:32 +0100143 mixed *data=get_dir(dirname+"*",GETDIR_NAMES|GETDIR_SIZES|GETDIR_DATES);
MG Mud User88f12472016-06-24 23:31:02 +0200144 if(!sizeof(data)) return ({});
Zesstra0f6a3022018-11-07 00:44:32 +0100145 mixed *files=({});
146
Zesstracc3f2502018-11-14 23:46:47 +0100147 while(1)
MG Mud User88f12472016-06-24 23:31:02 +0200148 {
Zesstra0f6a3022018-11-07 00:44:32 +0100149 mixed *tmp=({});
150 string base=data[BASENAME];
151 string fullname=dirname+base;
152 if (base!="." && base!=".."
153 && (!(mode==MODE_GREP && base=="RCS"))
154 && ((data[FILESIZE]==FSIZE_DIR
155 && sizeof(tmp=_get_files(fullname+"/",mask,mode,dest+base+"/"))
156 && mode!=MODE_RM)
157 || !mask
158 || sizeof(regexp(({ base }),mask, RE_TRADITIONAL))) )
MG Mud User88f12472016-06-24 23:31:02 +0200159 {
160 //DEBUG("_GF: ADDING FILE " + fullname);
161 files+= ({ data[0..2]+({ fullname,dirname,dest+base,
162 sizeof(tmp) }) });
163 }
164 if (sizeof(files)+sizeof(tmp)>MAX_ARRAY_SIZE)
165 raise_error("Zu viele Files (>3000)!! Abgebrochen!\n");
166 files+=tmp;
Zesstracc3f2502018-11-14 23:46:47 +0100167 if (sizeof(data) > 3)
168 data=data[3..];
169 else
170 break;
MG Mud User88f12472016-06-24 23:31:02 +0200171 }
Zesstraf22b6a82018-11-07 22:39:55 +0100172
MG Mud User88f12472016-06-24 23:31:02 +0200173 if(sizeof(files)>300&&!IS_ARCH(this_object()))
174 // Tod allen Laggern :o)
175 raise_error("Zu viele Files (>300)!! Abgebrochen!\n");
176 return files;
177}
178
179
180//
181// _get_matching: Rekursive Funktion zum ermitteln der Files, wenn mit Maske
182// gearbeitet wird (z.B. cp -m /bla/*/* /ziel *.c)
183// pathmask: Array, das die einzelnen Elemente der Maske (Arg1) enthaelt
184// (per efun:old_explode(arg1,"/"))
185// depth: Aktuelles Element von pathmask
186// path: implode(pathmask[0..depth],"/");
187// mode: Welche Daten werden benoetigt? (siehe magier.h)
188// flags: Welche Flags wurden gesetzt?
189// dest: Zielverzeichnis (beim kopieren/moven)
190// filemask: Maske, der die Files genuegen muessen
191// Rueckgabe: Alist mit den Dateiinformationen
192//
193
194private mixed *_get_matching(string *pathmask, int depth, string path,
195 int mode, int recursive, string dest,string filemask)
196{
MG Mud User88f12472016-06-24 23:31:02 +0200197 //DEBUG("_GM: PM: " + pathmask[depth]);
198 //DEBUG("_GM: FM: " + filemask);
Zesstraf22b6a82018-11-07 22:39:55 +0100199
200 // Pfad normalisieren (ggf. Platzhalter expandieren)
Zesstra07210ee2018-11-08 20:57:14 +0100201 string p=normalize_path(path+pathmask[depth++], getuid(), 1);
Zesstraf22b6a82018-11-07 22:39:55 +0100202 mixed *data=get_dir(p, GETDIR_NAMES|GETDIR_SIZES|GETDIR_DATES)||({});
Zesstra0f6a3022018-11-07 00:44:32 +0100203 if (!sizeof(data))
204 return ({});
205
Zesstraf22b6a82018-11-07 22:39:55 +0100206 // Bemerkung: path beginnt als "/" und wird nach und nach mit base
207 // verlaengert, daher muss das nicht explizit normalisiert werden. dest
208 // wurde bereits vom Aufrufer normalisiert und wird hier nur durchgereicht.
209
Zesstra0f6a3022018-11-07 00:44:32 +0100210 mixed *files=({});
MG Mud User88f12472016-06-24 23:31:02 +0200211 while(sizeof(data))
212 {
Zesstra0f6a3022018-11-07 00:44:32 +0100213 string base=data[BASENAME];
214 if (base=="." || base=="..")
MG Mud User88f12472016-06-24 23:31:02 +0200215 {
216 data=data[3..];
217 continue;
218 }
Zesstra0f6a3022018-11-07 00:44:32 +0100219 string full=path+base;
MG Mud User88f12472016-06-24 23:31:02 +0200220 //DEBUG("_GM: FULL: " + full);
Zesstra0f6a3022018-11-07 00:44:32 +0100221 mixed *tmp;
222 if ((data[FILESIZE]==FSIZE_DIR) && (sizeof(pathmask)>depth)
223 && (!(mode==MODE_GREP && base=="RCS")))
MG Mud User88f12472016-06-24 23:31:02 +0200224 {
225 //DEBUG("DESCEND INTO " + full);
226 tmp=_get_matching(pathmask,depth,full+"/",mode,recursive,
Zesstra0f6a3022018-11-07 00:44:32 +0100227 (recursive ? dest+base+"/" : dest),filemask);
MG Mud User88f12472016-06-24 23:31:02 +0200228 }
229 else tmp=({});
Zesstra0f6a3022018-11-07 00:44:32 +0100230
MG Mud User88f12472016-06-24 23:31:02 +0200231 //DEBUG("DEPTH: " + depth + " : " + sizeof(pathmask));
Zesstra0f6a3022018-11-07 00:44:32 +0100232 if((!filemask && (depth==sizeof(pathmask)))
233 || (filemask && (depth+2 > sizeof(pathmask))
234 && sizeof(regexp(({ base }),filemask,RE_TRADITIONAL)))
235 || ((mode==MODE_CP || mode==MODE_MV
236 || (filemask && (mode==MODE_RM)
237 && sizeof(regexp(({ base}),filemask,RE_TRADITIONAL))))
238 && sizeof(tmp)) )
MG Mud User88f12472016-06-24 23:31:02 +0200239 {
240 //DEBUG("ADDING: " + base+ " : "+ full );
241 files+=({ data[0..2]+({ full, path, dest+base,sizeof(tmp)}) });
242 }
Zesstra0f6a3022018-11-07 00:44:32 +0100243 if (sizeof(files) + sizeof(tmp) > MAX_ARRAY_SIZE)
MG Mud User88f12472016-06-24 23:31:02 +0200244 raise_error("Zu viele Files (>3000)!! Abgebrochen!\n");
245 files+=tmp;
Zesstra20b907f2018-11-08 00:34:03 +0100246 if(sizeof(data)>3)
247 data=data[3..];
248 else
249 break;
MG Mud User88f12472016-06-24 23:31:02 +0200250 }
251 if(sizeof(files)>300&&!IS_ARCH(this_object()))
252 // Tod allen Laggern!
253 raise_error("Zu viele Files (>300)!! Abgebrochen!\n");
254 return files;
255}
256
257
258//
259// get_files: Basisroutine zum Ermitteln der zu bearbeitenden Dateien
260// filename: Pfadmaske, Verzeichnisname, Dateiname, der bearbeitet werden
261// soll
262// mode: Welche Daten werden benoetigt? (siehe magier.h)
263// recursive: Auch Unterverzeichnisse bearbeiten?
264// dest: Wenn kopiert werden soll: wohin?
265// filemask: Maske, der die Dateinamen genuegen muessen
266// Rueckgabe: Alist mit den Dateiinformationen
267//
268
Zesstra0f6a3022018-11-07 00:44:32 +0100269static varargs mixed *get_files(string filename, int mode, int recursive,
270 string dest, string filemask)
271{
Zesstraf22b6a82018-11-07 22:39:55 +0100272 // Bemerkung. dest wurde bereits vom Aufrufer normalisiert.
273
MG Mud User88f12472016-06-24 23:31:02 +0200274 // DEBUG("GF: " + filename);
275 // DEBUG("REC: " + recursive + " MODE: " + mode);
276 // if (dest[<1..<1]!="/") DEBUG("DEST: " + dest);
277 if (filename=="/")
Zesstraf22b6a82018-11-07 22:39:55 +0100278 {
MG Mud User88f12472016-06-24 23:31:02 +0200279 switch (mode)
280 {
281 case MODE_LSA: return ({({ "", -2, 0,"","","",0 })});
282 default: if (!recursive) return ({});
283 break;
284 }
Zesstraf22b6a82018-11-07 22:39:55 +0100285 }
286
287 // Normalisiertes Pfadarray besorgen
Zesstra07210ee2018-11-08 20:57:14 +0100288 string *patharray=master()->path_array(filename);
Zesstraf22b6a82018-11-07 22:39:55 +0100289 // und daraus auch filename neu erzeugen
290 filename=implode(patharray, "/");
Zesstra0f6a3022018-11-07 00:44:32 +0100291
292 mixed *data=get_dir(filename,GETDIR_NAMES|GETDIR_SIZES|GETDIR_DATES)||({});
293 if(!sizeof(data)
294 && (mode==MODE_UPD || mode==MODE_MORE || mode==MODE_ED))
295 data=get_dir(filename+".c",GETDIR_NAMES|GETDIR_SIZES|GETDIR_DATES) || ({});
296
297 if ((mode==MODE_LSA||mode==MODE_LSB))
298 {
Zesstra0f6a3022018-11-07 00:44:32 +0100299 object vcompiler =
300 find_object(implode(patharray[0..<2],"/")+"/virtual_compiler");
Arathorn9845d112020-01-08 22:02:03 +0100301
302 object *vrooms = ({});
303 if (vcompiler)
304 vrooms = vcompiler->QueryObjects();
305
MG Mud User88f12472016-06-24 23:31:02 +0200306 map(vrooms,#'_vc_map,&data);
Zesstra0f6a3022018-11-07 00:44:32 +0100307 }
308 mixed *files=({});
MG Mud User88f12472016-06-24 23:31:02 +0200309 if (sizeof(data)) // passende files
310 {
311 mixed *subfiles;
312 subfiles=({});
Zesstra0f6a3022018-11-07 00:44:32 +0100313 string path=implode(patharray[0..<2],"/")+"/";
MG Mud User88f12472016-06-24 23:31:02 +0200314 while (sizeof(data))
315 {
316 subfiles=({});
Zesstra0f6a3022018-11-07 00:44:32 +0100317 string base=data[BASENAME];
MG Mud User88f12472016-06-24 23:31:02 +0200318 if (mode==MODE_LSB||(base!="."&&base!=".."))
319 {
320 //DEBUG("PATH: " + path+" BASE: " + base + " MODE: " + mode);
Zesstra0f6a3022018-11-07 00:44:32 +0100321 string full=path+base;
322 string dest2;
323 if (dest=="/" || file_size(dest[0..<2])==FSIZE_DIR)
324 dest2 = dest+base;
325 else
326 dest2 = dest[0..<2];
327
MG Mud User88f12472016-06-24 23:31:02 +0200328 //DEBUG("DEST: " + dest);
Zesstra0f6a3022018-11-07 00:44:32 +0100329 if (recursive && data[FILESIZE]==FSIZE_DIR) // Verzeichnis, Rekursiv
MG Mud User88f12472016-06-24 23:31:02 +0200330 subfiles=_get_files(full+"/",filemask,mode,dest2+"/");
Zesstra0f6a3022018-11-07 00:44:32 +0100331 if (!(filemask && !sizeof(subfiles)
332 && !sizeof(regexp(({ base }),filemask,RE_TRADITIONAL))))
MG Mud User88f12472016-06-24 23:31:02 +0200333 {
Zesstra0f6a3022018-11-07 00:44:32 +0100334 if (!filemask || mode!=MODE_RM)
MG Mud User88f12472016-06-24 23:31:02 +0200335 files+=({ data[0..2]+({ full, path, dest2,sizeof(subfiles)}) });
Zesstra0f6a3022018-11-07 00:44:32 +0100336 if (sizeof(files)+sizeof(subfiles) > MAX_ARRAY_SIZE)
MG Mud User88f12472016-06-24 23:31:02 +0200337 raise_error("Zu viele Files (>3000)!! Abgebrochen!\n");
338 files+=subfiles;
339 }
340 }
Zesstra0524def2018-11-07 22:48:37 +0100341 if (sizeof(data)>3)
342 data=data[3..];
343 else
344 break;
MG Mud User88f12472016-06-24 23:31:02 +0200345 }
346 return files;
347 }
348// File existiert nicht -> Wildcard oder tatsaechlich nicht existent
349// Behandeln je nach mode
350 switch(mode)
351 {
352 case MODE_CP:
353 case MODE_MV:
354 case MODE_CD:
355 case MODE_LSA:
356 files=_get_matching(patharray+(filemask?({ "*" }):({})),1,"/",mode,
357 recursive,dest,filemask);
358 break;
359 default: break;
360 }
361 return files;
362}
363
364
365//
366// file_list(): Liste der Fileinformationen
367// files: Array der Filenamen MIT Wildcards
368// mode: Welche Daten werden benoetigt
369// recursive: Rekursiv listen?
370// dest: Zielverzeichnis
371// mask: Maske (regexp, wenn definiert)
372// Rueckgabe: Liste der betroffenen Files
373//
374
Zesstra0f6a3022018-11-07 00:44:32 +0100375static varargs mixed *file_list(string *files, int mode, int recursive,
376 string dest, string mask)
MG Mud User88f12472016-06-24 23:31:02 +0200377{
Zesstra0f6a3022018-11-07 00:44:32 +0100378 string *list=({});
MG Mud User88f12472016-06-24 23:31:02 +0200379 if (mask) mask=glob2regexp(mask);
Zesstraf22b6a82018-11-07 22:39:55 +0100380 // dest muss einmal normalisiert werden, dann muss es das in den gerufenen
381 // (rekursiven) Funktionen nicht immer nochmal gemacht werden.
Zesstra07210ee2018-11-08 20:57:14 +0100382 dest=normalize_path(dest, getuid(), 1);
Zesstraf22b6a82018-11-07 22:39:55 +0100383
Zesstra0f6a3022018-11-07 00:44:32 +0100384 foreach(string file: files)
MG Mud User88f12472016-06-24 23:31:02 +0200385 {
386 // Abschliessenden / von Pfadnamen abschneiden, weil in diesem Fall
387 // die Inhalte der ersten Unterverzeichnisebene mit ausgegeben
388 // wurden. Ursache hierfuer ist ein Fix an full_path_array() im
389 // Masterobjekt, der dazu fuehrte, dass get_dir() den abschliessenden /
390 // des uebergebenen Pfades jetzt korrekt behandelt. \o/
Zesstra0f6a3022018-11-07 00:44:32 +0100391 if ( sizeof(file) > 1 && file[<1] == '/' )
MG Mud User88f12472016-06-24 23:31:02 +0200392 {
Zesstra0f6a3022018-11-07 00:44:32 +0100393 file = file[0..<2];
394 if ( !sizeof(file) )
MG Mud User88f12472016-06-24 23:31:02 +0200395 continue;
396 }
Zesstra0f6a3022018-11-07 00:44:32 +0100397 string err=catch(list += get_files(file,mode,recursive,dest,mask));
398 if (err)
MG Mud User88f12472016-06-24 23:31:02 +0200399 {
400 printf("Fehler aufgetreten: %s\n",err);
401 log_file(SHELLLOG("FILE_LIST"),
402 sprintf("%s fuehrte folgendes Kommando aus: (Zeit: %s)\n"
403 " >>%s %s<<\n"
404 " Folgender Fehler trat dabei auf:\n"
405 " %s\n\n",
406 capitalize(getuid())||"<Unbekannt>",dtime(time()),
407 query_verb()||"*",_unparsed_args()||"*",err||"*"));
408 return ({});
409 }
410 }
411 return list;
412}