blob: 73320e55d9433322c771e1b956185ab0a0282ce2 [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
3#pragma range_check, pedantic
MG Mud User88f12472016-06-24 23:31:02 +02004#pragma no_clone
MG Mud User88f12472016-06-24 23:31:02 +02005
6#include <files.h>
7#include <wizlevels.h>
8#include <logging.h>
9#include <regexp.h>
10#define NEED_PROTOTYPES
11#include <magier.h>
12#include <thing/properties.h>
13#include <player.h>
14
15//
16// glob2regexp: Argument von glob in Regexp umwandeln
17// str: Argument (als Referenz)
18//
19
20static string glob2regexp(string str)
21{
22 str=regreplace(str,"([\\.\\^\\$\\[\\]\\(\\)])","\\\\\\1",RE_TRADITIONAL|1);
23 str=regreplace(str,"\\*",".*",RE_TRADITIONAL|1);
24 return sprintf("^%s$",regreplace(str,"?",".",RE_TRADITIONAL|1));
25}
26
27//
28// to_filename: Argument in Dateinamen umwandeln
29// str: Argument
30// Rueckgabe: Dateiname
31//
32
33static mixed to_filename(string str)
34{
35 string *tmp,p,newfn;
36 int i;
37// Testen ob .. in einem Filenamenabschnitt, falls Version <3.2.5
38 tmp=explode(str,"/");
39// Testen auf Pfadvariable
40 if (sizeof(tmp[0]) && tmp[0][0]=='$'
41 && m_contains(&p,QueryProp(P_VARIABLES),tmp[0][1..]))
42 tmp[0]=p;
43// Pfad absolut machen (Hat danach noch Wildcards drinnen) oder auch nicht
44 return master()->make_path_absolute(implode(tmp,"/"));
45}
46
47
48//
49// _parseargs(): Kommandozeilenabschnitt parsen
50// str: Kommandozeilenabschnitt
51// line: Array von geparsten Kommandozeilenabschnitten (Referenz)
52// flags: Als Referenz uebergebener Flag-Wert
53// opts: Erlaubte Flags
54// build_fn: Filenamen aendern
55//
56
57private void _parseargs(string str, string *line,int flags,string opts,
58 int build_fn )
59{
60
61// Strings in "" erhalten
62 if(str[0] == "\""[0])
63 {
64 line += ({ str[1..<2] });
65 return;
66 }
67// Flags parsen
68 if(str[0] == '-')
69 {
70 int i,j;
71 i=sizeof(str);
72 while(i--)
73 if (str[i]!='-')
74 {
75 if((j = member(opts, str[i])) != -1)
76 flags |= (1 << j);
77 else
78 {
79 flags=-1;
80 printf("Das Flag '-%c' wird von dem Befehl '%s' nicht "
81 "unterstuetzt.\n",str[i],query_verb()||"");
82 }
83 }
84 return;
85 }
86 if (build_fn)
87 {
88 if (str=(string)to_filename(str)) line+=({ str });
89 }
90 else
91 line+= ({ str });
92}
93
94
95//
96// parseargs() - zerlegt Kommandozeile in ein Array:
97// cmdline: Kommandozeile
98// flags: Als Referenz uebergebener leerer Integerwert. Enthaelt danach
99// die Flags
100// opts: String mit erlaubten Optionen
101// build_fn: Filenamen umbauen?
102// Rueckgabe: Array der Kommandozeilenargumente (ausser Flags)
103//
104
105static string *parseargs(string cmdline,int flags, string opts,int build_fn)
106{
107 int i;
108 string *line;
109 line=({});
110 if (!sizeof(cmdline)) return ({});
111 map(regexplode(cmdline,"[\"][^\"]*[\"]| ", RE_TRADITIONAL)-({" ", ""}),
112 #'_parseargs, &line, &flags,opts, build_fn);
113 return line - ({""});
114}
115
116//
117// _vc_map: VC-Objektnamen zu Arrays umwandeln ({ name, -1, program_time() })
118//
119
120private int *_vc_map(object ob,mixed *list)
121{
122 list+=({ explode(object_name(ob),"/")[<1],-1,program_time(ob) });
123 return 0;
124}
125
126
127//
128// _get_files: Filedaten beim Rekursiven kopieren erstellen
129// dirname: Bearbeitetes Verzeichnis
130// mask: Maske, der Dateinamen genuegen muessen
131// mode: Welche Daten werden benoetigt? (siehe magier.h)
132// dest: In welches Verzeichnis soll kopiert werden?
133// Rueckgabe: Alist mit den Dateiinformationen
134//
135
Zesstra0f6a3022018-11-07 00:44:32 +0100136private varargs mixed *_get_files(string dirname,string mask,int mode,
137 string dest)
MG Mud User88f12472016-06-24 23:31:02 +0200138{
MG Mud User88f12472016-06-24 23:31:02 +0200139 //DEBUG("_GF: DIRNAME " + dirname);
Zesstra0f6a3022018-11-07 00:44:32 +0100140 mixed *data=get_dir(dirname+"*",GETDIR_NAMES|GETDIR_SIZES|GETDIR_DATES);
MG Mud User88f12472016-06-24 23:31:02 +0200141 if(!sizeof(data)) return ({});
Zesstra0f6a3022018-11-07 00:44:32 +0100142 mixed *files=({});
143
MG Mud User88f12472016-06-24 23:31:02 +0200144 while(sizeof(data))
145 {
Zesstra0f6a3022018-11-07 00:44:32 +0100146 mixed *tmp=({});
147 string base=data[BASENAME];
148 string fullname=dirname+base;
149 if (base!="." && base!=".."
150 && (!(mode==MODE_GREP && base=="RCS"))
151 && ((data[FILESIZE]==FSIZE_DIR
152 && sizeof(tmp=_get_files(fullname+"/",mask,mode,dest+base+"/"))
153 && mode!=MODE_RM)
154 || !mask
155 || sizeof(regexp(({ base }),mask, RE_TRADITIONAL))) )
MG Mud User88f12472016-06-24 23:31:02 +0200156 {
157 //DEBUG("_GF: ADDING FILE " + fullname);
158 files+= ({ data[0..2]+({ fullname,dirname,dest+base,
159 sizeof(tmp) }) });
160 }
161 if (sizeof(files)+sizeof(tmp)>MAX_ARRAY_SIZE)
162 raise_error("Zu viele Files (>3000)!! Abgebrochen!\n");
163 files+=tmp;
164 data=data[3..];
165 }
166
167 if(sizeof(files)>300&&!IS_ARCH(this_object()))
168 // Tod allen Laggern :o)
169 raise_error("Zu viele Files (>300)!! Abgebrochen!\n");
170 return files;
171}
172
173
174//
175// _get_matching: Rekursive Funktion zum ermitteln der Files, wenn mit Maske
176// gearbeitet wird (z.B. cp -m /bla/*/* /ziel *.c)
177// pathmask: Array, das die einzelnen Elemente der Maske (Arg1) enthaelt
178// (per efun:old_explode(arg1,"/"))
179// depth: Aktuelles Element von pathmask
180// path: implode(pathmask[0..depth],"/");
181// mode: Welche Daten werden benoetigt? (siehe magier.h)
182// flags: Welche Flags wurden gesetzt?
183// dest: Zielverzeichnis (beim kopieren/moven)
184// filemask: Maske, der die Files genuegen muessen
185// Rueckgabe: Alist mit den Dateiinformationen
186//
187
188private mixed *_get_matching(string *pathmask, int depth, string path,
189 int mode, int recursive, string dest,string filemask)
190{
MG Mud User88f12472016-06-24 23:31:02 +0200191 //DEBUG("_GM: PM: " + pathmask[depth]);
192 //DEBUG("_GM: FM: " + filemask);
Zesstra0f6a3022018-11-07 00:44:32 +0100193 mixed *data=get_dir(path+pathmask[depth++],GETDIR_NAMES|GETDIR_SIZES|
194 GETDIR_DATES)||({});
195 if (!sizeof(data))
196 return ({});
197
198 mixed *files=({});
MG Mud User88f12472016-06-24 23:31:02 +0200199 while(sizeof(data))
200 {
Zesstra0f6a3022018-11-07 00:44:32 +0100201 string base=data[BASENAME];
202 if (base=="." || base=="..")
MG Mud User88f12472016-06-24 23:31:02 +0200203 {
204 data=data[3..];
205 continue;
206 }
Zesstra0f6a3022018-11-07 00:44:32 +0100207 string full=path+base;
MG Mud User88f12472016-06-24 23:31:02 +0200208 //DEBUG("_GM: FULL: " + full);
Zesstra0f6a3022018-11-07 00:44:32 +0100209 mixed *tmp;
210 if ((data[FILESIZE]==FSIZE_DIR) && (sizeof(pathmask)>depth)
211 && (!(mode==MODE_GREP && base=="RCS")))
MG Mud User88f12472016-06-24 23:31:02 +0200212 {
213 //DEBUG("DESCEND INTO " + full);
214 tmp=_get_matching(pathmask,depth,full+"/",mode,recursive,
Zesstra0f6a3022018-11-07 00:44:32 +0100215 (recursive ? dest+base+"/" : dest),filemask);
MG Mud User88f12472016-06-24 23:31:02 +0200216 }
217 else tmp=({});
Zesstra0f6a3022018-11-07 00:44:32 +0100218
MG Mud User88f12472016-06-24 23:31:02 +0200219 //DEBUG("DEPTH: " + depth + " : " + sizeof(pathmask));
Zesstra0f6a3022018-11-07 00:44:32 +0100220 if((!filemask && (depth==sizeof(pathmask)))
221 || (filemask && (depth+2 > sizeof(pathmask))
222 && sizeof(regexp(({ base }),filemask,RE_TRADITIONAL)))
223 || ((mode==MODE_CP || mode==MODE_MV
224 || (filemask && (mode==MODE_RM)
225 && sizeof(regexp(({ base}),filemask,RE_TRADITIONAL))))
226 && sizeof(tmp)) )
MG Mud User88f12472016-06-24 23:31:02 +0200227 {
228 //DEBUG("ADDING: " + base+ " : "+ full );
229 files+=({ data[0..2]+({ full, path, dest+base,sizeof(tmp)}) });
230 }
Zesstra0f6a3022018-11-07 00:44:32 +0100231 if (sizeof(files) + sizeof(tmp) > MAX_ARRAY_SIZE)
MG Mud User88f12472016-06-24 23:31:02 +0200232 raise_error("Zu viele Files (>3000)!! Abgebrochen!\n");
233 files+=tmp;
234 data=data[3..];
235 }
236 if(sizeof(files)>300&&!IS_ARCH(this_object()))
237 // Tod allen Laggern!
238 raise_error("Zu viele Files (>300)!! Abgebrochen!\n");
239 return files;
240}
241
242
243//
244// get_files: Basisroutine zum Ermitteln der zu bearbeitenden Dateien
245// filename: Pfadmaske, Verzeichnisname, Dateiname, der bearbeitet werden
246// soll
247// mode: Welche Daten werden benoetigt? (siehe magier.h)
248// recursive: Auch Unterverzeichnisse bearbeiten?
249// dest: Wenn kopiert werden soll: wohin?
250// filemask: Maske, der die Dateinamen genuegen muessen
251// Rueckgabe: Alist mit den Dateiinformationen
252//
253
Zesstra0f6a3022018-11-07 00:44:32 +0100254static varargs mixed *get_files(string filename, int mode, int recursive,
255 string dest, string filemask)
256{
MG Mud User88f12472016-06-24 23:31:02 +0200257 // DEBUG("GF: " + filename);
258 // DEBUG("REC: " + recursive + " MODE: " + mode);
259 // if (dest[<1..<1]!="/") DEBUG("DEST: " + dest);
260 if (filename=="/")
261 {
262 switch (mode)
263 {
264 case MODE_LSA: return ({({ "", -2, 0,"","","",0 })});
265 default: if (!recursive) return ({});
266 break;
267 }
268 }
Zesstra0f6a3022018-11-07 00:44:32 +0100269 string *patharray=explode(filename,"/");
270
271 mixed *data=get_dir(filename,GETDIR_NAMES|GETDIR_SIZES|GETDIR_DATES)||({});
272 if(!sizeof(data)
273 && (mode==MODE_UPD || mode==MODE_MORE || mode==MODE_ED))
274 data=get_dir(filename+".c",GETDIR_NAMES|GETDIR_SIZES|GETDIR_DATES) || ({});
275
276 if ((mode==MODE_LSA||mode==MODE_LSB))
277 {
278 string *vrooms;
279 object vcompiler =
280 find_object(implode(patharray[0..<2],"/")+"/virtual_compiler");
281 if (vcompiler && pointerp(vrooms=(mixed *)vcompiler->QueryObjects()))
MG Mud User88f12472016-06-24 23:31:02 +0200282 map(vrooms,#'_vc_map,&data);
Zesstra0f6a3022018-11-07 00:44:32 +0100283 }
284 mixed *files=({});
MG Mud User88f12472016-06-24 23:31:02 +0200285 if (sizeof(data)) // passende files
286 {
287 mixed *subfiles;
288 subfiles=({});
Zesstra0f6a3022018-11-07 00:44:32 +0100289 string path=implode(patharray[0..<2],"/")+"/";
MG Mud User88f12472016-06-24 23:31:02 +0200290 while (sizeof(data))
291 {
292 subfiles=({});
Zesstra0f6a3022018-11-07 00:44:32 +0100293 string base=data[BASENAME];
MG Mud User88f12472016-06-24 23:31:02 +0200294 if (mode==MODE_LSB||(base!="."&&base!=".."))
295 {
296 //DEBUG("PATH: " + path+" BASE: " + base + " MODE: " + mode);
Zesstra0f6a3022018-11-07 00:44:32 +0100297 string full=path+base;
298 string dest2;
299 if (dest=="/" || file_size(dest[0..<2])==FSIZE_DIR)
300 dest2 = dest+base;
301 else
302 dest2 = dest[0..<2];
303
MG Mud User88f12472016-06-24 23:31:02 +0200304 //DEBUG("DEST: " + dest);
Zesstra0f6a3022018-11-07 00:44:32 +0100305 if (recursive && data[FILESIZE]==FSIZE_DIR) // Verzeichnis, Rekursiv
MG Mud User88f12472016-06-24 23:31:02 +0200306 subfiles=_get_files(full+"/",filemask,mode,dest2+"/");
Zesstra0f6a3022018-11-07 00:44:32 +0100307 if (!(filemask && !sizeof(subfiles)
308 && !sizeof(regexp(({ base }),filemask,RE_TRADITIONAL))))
MG Mud User88f12472016-06-24 23:31:02 +0200309 {
Zesstra0f6a3022018-11-07 00:44:32 +0100310 if (!filemask || mode!=MODE_RM)
MG Mud User88f12472016-06-24 23:31:02 +0200311 files+=({ data[0..2]+({ full, path, dest2,sizeof(subfiles)}) });
Zesstra0f6a3022018-11-07 00:44:32 +0100312 if (sizeof(files)+sizeof(subfiles) > MAX_ARRAY_SIZE)
MG Mud User88f12472016-06-24 23:31:02 +0200313 raise_error("Zu viele Files (>3000)!! Abgebrochen!\n");
314 files+=subfiles;
315 }
316 }
317 data=data[3..];
318 }
319 return files;
320 }
321// File existiert nicht -> Wildcard oder tatsaechlich nicht existent
322// Behandeln je nach mode
323 switch(mode)
324 {
325 case MODE_CP:
326 case MODE_MV:
327 case MODE_CD:
328 case MODE_LSA:
329 files=_get_matching(patharray+(filemask?({ "*" }):({})),1,"/",mode,
330 recursive,dest,filemask);
331 break;
332 default: break;
333 }
334 return files;
335}
336
337
338//
339// file_list(): Liste der Fileinformationen
340// files: Array der Filenamen MIT Wildcards
341// mode: Welche Daten werden benoetigt
342// recursive: Rekursiv listen?
343// dest: Zielverzeichnis
344// mask: Maske (regexp, wenn definiert)
345// Rueckgabe: Liste der betroffenen Files
346//
347
Zesstra0f6a3022018-11-07 00:44:32 +0100348static varargs mixed *file_list(string *files, int mode, int recursive,
349 string dest, string mask)
MG Mud User88f12472016-06-24 23:31:02 +0200350{
Zesstra0f6a3022018-11-07 00:44:32 +0100351 string *list=({});
MG Mud User88f12472016-06-24 23:31:02 +0200352 if (mask) mask=glob2regexp(mask);
Zesstra0f6a3022018-11-07 00:44:32 +0100353 foreach(string file: files)
MG Mud User88f12472016-06-24 23:31:02 +0200354 {
355 // Abschliessenden / von Pfadnamen abschneiden, weil in diesem Fall
356 // die Inhalte der ersten Unterverzeichnisebene mit ausgegeben
357 // wurden. Ursache hierfuer ist ein Fix an full_path_array() im
358 // Masterobjekt, der dazu fuehrte, dass get_dir() den abschliessenden /
359 // des uebergebenen Pfades jetzt korrekt behandelt. \o/
Zesstra0f6a3022018-11-07 00:44:32 +0100360 if ( sizeof(file) > 1 && file[<1] == '/' )
MG Mud User88f12472016-06-24 23:31:02 +0200361 {
Zesstra0f6a3022018-11-07 00:44:32 +0100362 file = file[0..<2];
363 if ( !sizeof(file) )
MG Mud User88f12472016-06-24 23:31:02 +0200364 continue;
365 }
Zesstra0f6a3022018-11-07 00:44:32 +0100366 string err=catch(list += get_files(file,mode,recursive,dest,mask));
367 if (err)
MG Mud User88f12472016-06-24 23:31:02 +0200368 {
369 printf("Fehler aufgetreten: %s\n",err);
370 log_file(SHELLLOG("FILE_LIST"),
371 sprintf("%s fuehrte folgendes Kommando aus: (Zeit: %s)\n"
372 " >>%s %s<<\n"
373 " Folgender Fehler trat dabei auf:\n"
374 " %s\n\n",
375 capitalize(getuid())||"<Unbekannt>",dtime(time()),
376 query_verb()||"*",_unparsed_args()||"*",err||"*"));
377 return ({});
378 }
379 }
380 return list;
381}