blob: 72208e3430c559445ab392bd79c719f340297eb9 [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>
Zesstraf22b6a82018-11-07 22:39:55 +010010#include <defines.h>
MG Mud User88f12472016-06-24 23:31:02 +020011#define NEED_PROTOTYPES
12#include <magier.h>
13#include <thing/properties.h>
14#include <player.h>
15
16//
17// glob2regexp: Argument von glob in Regexp umwandeln
18// str: Argument (als Referenz)
19//
20
21static string glob2regexp(string str)
22{
23 str=regreplace(str,"([\\.\\^\\$\\[\\]\\(\\)])","\\\\\\1",RE_TRADITIONAL|1);
24 str=regreplace(str,"\\*",".*",RE_TRADITIONAL|1);
25 return sprintf("^%s$",regreplace(str,"?",".",RE_TRADITIONAL|1));
26}
27
28//
29// to_filename: Argument in Dateinamen umwandeln
30// str: Argument
31// Rueckgabe: Dateiname
32//
33
34static mixed to_filename(string str)
35{
36 string *tmp,p,newfn;
37 int i;
38// Testen ob .. in einem Filenamenabschnitt, falls Version <3.2.5
39 tmp=explode(str,"/");
40// Testen auf Pfadvariable
41 if (sizeof(tmp[0]) && tmp[0][0]=='$'
42 && m_contains(&p,QueryProp(P_VARIABLES),tmp[0][1..]))
43 tmp[0]=p;
44// Pfad absolut machen (Hat danach noch Wildcards drinnen) oder auch nicht
45 return master()->make_path_absolute(implode(tmp,"/"));
46}
47
48
49//
50// _parseargs(): Kommandozeilenabschnitt parsen
51// str: Kommandozeilenabschnitt
52// line: Array von geparsten Kommandozeilenabschnitten (Referenz)
53// flags: Als Referenz uebergebener Flag-Wert
54// opts: Erlaubte Flags
55// build_fn: Filenamen aendern
56//
57
58private void _parseargs(string str, string *line,int flags,string opts,
59 int build_fn )
60{
61
62// Strings in "" erhalten
63 if(str[0] == "\""[0])
64 {
65 line += ({ str[1..<2] });
66 return;
67 }
68// Flags parsen
69 if(str[0] == '-')
70 {
71 int i,j;
72 i=sizeof(str);
73 while(i--)
74 if (str[i]!='-')
75 {
76 if((j = member(opts, str[i])) != -1)
77 flags |= (1 << j);
78 else
79 {
80 flags=-1;
81 printf("Das Flag '-%c' wird von dem Befehl '%s' nicht "
82 "unterstuetzt.\n",str[i],query_verb()||"");
83 }
84 }
85 return;
86 }
87 if (build_fn)
88 {
89 if (str=(string)to_filename(str)) line+=({ str });
90 }
91 else
92 line+= ({ str });
93}
94
95
96//
97// parseargs() - zerlegt Kommandozeile in ein Array:
98// cmdline: Kommandozeile
99// flags: Als Referenz uebergebener leerer Integerwert. Enthaelt danach
100// die Flags
101// opts: String mit erlaubten Optionen
102// build_fn: Filenamen umbauen?
103// Rueckgabe: Array der Kommandozeilenargumente (ausser Flags)
104//
105
106static string *parseargs(string cmdline,int flags, string opts,int build_fn)
107{
108 int i;
109 string *line;
110 line=({});
111 if (!sizeof(cmdline)) return ({});
112 map(regexplode(cmdline,"[\"][^\"]*[\"]| ", RE_TRADITIONAL)-({" ", ""}),
113 #'_parseargs, &line, &flags,opts, build_fn);
114 return line - ({""});
115}
116
117//
118// _vc_map: VC-Objektnamen zu Arrays umwandeln ({ name, -1, program_time() })
119//
120
121private int *_vc_map(object ob,mixed *list)
122{
123 list+=({ explode(object_name(ob),"/")[<1],-1,program_time(ob) });
124 return 0;
125}
126
127
128//
129// _get_files: Filedaten beim Rekursiven kopieren erstellen
130// dirname: Bearbeitetes Verzeichnis
131// mask: Maske, der Dateinamen genuegen muessen
132// mode: Welche Daten werden benoetigt? (siehe magier.h)
133// dest: In welches Verzeichnis soll kopiert werden?
134// Rueckgabe: Alist mit den Dateiinformationen
135//
136
Zesstra0f6a3022018-11-07 00:44:32 +0100137private varargs mixed *_get_files(string dirname,string mask,int mode,
138 string dest)
MG Mud User88f12472016-06-24 23:31:02 +0200139{
MG Mud User88f12472016-06-24 23:31:02 +0200140 //DEBUG("_GF: DIRNAME " + dirname);
Zesstra0f6a3022018-11-07 00:44:32 +0100141 mixed *data=get_dir(dirname+"*",GETDIR_NAMES|GETDIR_SIZES|GETDIR_DATES);
MG Mud User88f12472016-06-24 23:31:02 +0200142 if(!sizeof(data)) return ({});
Zesstra0f6a3022018-11-07 00:44:32 +0100143 mixed *files=({});
144
MG Mud User88f12472016-06-24 23:31:02 +0200145 while(sizeof(data))
146 {
Zesstra0f6a3022018-11-07 00:44:32 +0100147 mixed *tmp=({});
148 string base=data[BASENAME];
149 string fullname=dirname+base;
150 if (base!="." && base!=".."
151 && (!(mode==MODE_GREP && base=="RCS"))
152 && ((data[FILESIZE]==FSIZE_DIR
153 && sizeof(tmp=_get_files(fullname+"/",mask,mode,dest+base+"/"))
154 && mode!=MODE_RM)
155 || !mask
156 || sizeof(regexp(({ base }),mask, RE_TRADITIONAL))) )
MG Mud User88f12472016-06-24 23:31:02 +0200157 {
158 //DEBUG("_GF: ADDING FILE " + fullname);
159 files+= ({ data[0..2]+({ fullname,dirname,dest+base,
160 sizeof(tmp) }) });
161 }
162 if (sizeof(files)+sizeof(tmp)>MAX_ARRAY_SIZE)
163 raise_error("Zu viele Files (>3000)!! Abgebrochen!\n");
164 files+=tmp;
165 data=data[3..];
166 }
Zesstraf22b6a82018-11-07 22:39:55 +0100167
MG Mud User88f12472016-06-24 23:31:02 +0200168 if(sizeof(files)>300&&!IS_ARCH(this_object()))
169 // Tod allen Laggern :o)
170 raise_error("Zu viele Files (>300)!! Abgebrochen!\n");
171 return files;
172}
173
174
175//
176// _get_matching: Rekursive Funktion zum ermitteln der Files, wenn mit Maske
177// gearbeitet wird (z.B. cp -m /bla/*/* /ziel *.c)
178// pathmask: Array, das die einzelnen Elemente der Maske (Arg1) enthaelt
179// (per efun:old_explode(arg1,"/"))
180// depth: Aktuelles Element von pathmask
181// path: implode(pathmask[0..depth],"/");
182// mode: Welche Daten werden benoetigt? (siehe magier.h)
183// flags: Welche Flags wurden gesetzt?
184// dest: Zielverzeichnis (beim kopieren/moven)
185// filemask: Maske, der die Files genuegen muessen
186// Rueckgabe: Alist mit den Dateiinformationen
187//
188
189private mixed *_get_matching(string *pathmask, int depth, string path,
190 int mode, int recursive, string dest,string filemask)
191{
MG Mud User88f12472016-06-24 23:31:02 +0200192 //DEBUG("_GM: PM: " + pathmask[depth]);
193 //DEBUG("_GM: FM: " + filemask);
Zesstraf22b6a82018-11-07 22:39:55 +0100194
195 // Pfad normalisieren (ggf. Platzhalter expandieren)
196 string p=master()->normalize_path(path+pathmask[depth++],
197 getuid(RPL||PL), 1);
198 mixed *data=get_dir(p, GETDIR_NAMES|GETDIR_SIZES|GETDIR_DATES)||({});
Zesstra0f6a3022018-11-07 00:44:32 +0100199 if (!sizeof(data))
200 return ({});
201
Zesstraf22b6a82018-11-07 22:39:55 +0100202 // Bemerkung: path beginnt als "/" und wird nach und nach mit base
203 // verlaengert, daher muss das nicht explizit normalisiert werden. dest
204 // wurde bereits vom Aufrufer normalisiert und wird hier nur durchgereicht.
205
Zesstra0f6a3022018-11-07 00:44:32 +0100206 mixed *files=({});
MG Mud User88f12472016-06-24 23:31:02 +0200207 while(sizeof(data))
208 {
Zesstra0f6a3022018-11-07 00:44:32 +0100209 string base=data[BASENAME];
210 if (base=="." || base=="..")
MG Mud User88f12472016-06-24 23:31:02 +0200211 {
212 data=data[3..];
213 continue;
214 }
Zesstra0f6a3022018-11-07 00:44:32 +0100215 string full=path+base;
MG Mud User88f12472016-06-24 23:31:02 +0200216 //DEBUG("_GM: FULL: " + full);
Zesstra0f6a3022018-11-07 00:44:32 +0100217 mixed *tmp;
218 if ((data[FILESIZE]==FSIZE_DIR) && (sizeof(pathmask)>depth)
219 && (!(mode==MODE_GREP && base=="RCS")))
MG Mud User88f12472016-06-24 23:31:02 +0200220 {
221 //DEBUG("DESCEND INTO " + full);
222 tmp=_get_matching(pathmask,depth,full+"/",mode,recursive,
Zesstra0f6a3022018-11-07 00:44:32 +0100223 (recursive ? dest+base+"/" : dest),filemask);
MG Mud User88f12472016-06-24 23:31:02 +0200224 }
225 else tmp=({});
Zesstra0f6a3022018-11-07 00:44:32 +0100226
MG Mud User88f12472016-06-24 23:31:02 +0200227 //DEBUG("DEPTH: " + depth + " : " + sizeof(pathmask));
Zesstra0f6a3022018-11-07 00:44:32 +0100228 if((!filemask && (depth==sizeof(pathmask)))
229 || (filemask && (depth+2 > sizeof(pathmask))
230 && sizeof(regexp(({ base }),filemask,RE_TRADITIONAL)))
231 || ((mode==MODE_CP || mode==MODE_MV
232 || (filemask && (mode==MODE_RM)
233 && sizeof(regexp(({ base}),filemask,RE_TRADITIONAL))))
234 && sizeof(tmp)) )
MG Mud User88f12472016-06-24 23:31:02 +0200235 {
236 //DEBUG("ADDING: " + base+ " : "+ full );
237 files+=({ data[0..2]+({ full, path, dest+base,sizeof(tmp)}) });
238 }
Zesstra0f6a3022018-11-07 00:44:32 +0100239 if (sizeof(files) + sizeof(tmp) > MAX_ARRAY_SIZE)
MG Mud User88f12472016-06-24 23:31:02 +0200240 raise_error("Zu viele Files (>3000)!! Abgebrochen!\n");
241 files+=tmp;
Zesstra20b907f2018-11-08 00:34:03 +0100242 if(sizeof(data)>3)
243 data=data[3..];
244 else
245 break;
MG Mud User88f12472016-06-24 23:31:02 +0200246 }
247 if(sizeof(files)>300&&!IS_ARCH(this_object()))
248 // Tod allen Laggern!
249 raise_error("Zu viele Files (>300)!! Abgebrochen!\n");
250 return files;
251}
252
253
254//
255// get_files: Basisroutine zum Ermitteln der zu bearbeitenden Dateien
256// filename: Pfadmaske, Verzeichnisname, Dateiname, der bearbeitet werden
257// soll
258// mode: Welche Daten werden benoetigt? (siehe magier.h)
259// recursive: Auch Unterverzeichnisse bearbeiten?
260// dest: Wenn kopiert werden soll: wohin?
261// filemask: Maske, der die Dateinamen genuegen muessen
262// Rueckgabe: Alist mit den Dateiinformationen
263//
264
Zesstra0f6a3022018-11-07 00:44:32 +0100265static varargs mixed *get_files(string filename, int mode, int recursive,
266 string dest, string filemask)
267{
Zesstraf22b6a82018-11-07 22:39:55 +0100268 // Bemerkung. dest wurde bereits vom Aufrufer normalisiert.
269
MG Mud User88f12472016-06-24 23:31:02 +0200270 // DEBUG("GF: " + filename);
271 // DEBUG("REC: " + recursive + " MODE: " + mode);
272 // if (dest[<1..<1]!="/") DEBUG("DEST: " + dest);
273 if (filename=="/")
Zesstraf22b6a82018-11-07 22:39:55 +0100274 {
MG Mud User88f12472016-06-24 23:31:02 +0200275 switch (mode)
276 {
277 case MODE_LSA: return ({({ "", -2, 0,"","","",0 })});
278 default: if (!recursive) return ({});
279 break;
280 }
Zesstraf22b6a82018-11-07 22:39:55 +0100281 }
282
283 // Normalisiertes Pfadarray besorgen
284 string *patharray=master()->path_array(filename, getuid(RPL||PL), 1);
285 // und daraus auch filename neu erzeugen
286 filename=implode(patharray, "/");
Zesstra0f6a3022018-11-07 00:44:32 +0100287
288 mixed *data=get_dir(filename,GETDIR_NAMES|GETDIR_SIZES|GETDIR_DATES)||({});
289 if(!sizeof(data)
290 && (mode==MODE_UPD || mode==MODE_MORE || mode==MODE_ED))
291 data=get_dir(filename+".c",GETDIR_NAMES|GETDIR_SIZES|GETDIR_DATES) || ({});
292
293 if ((mode==MODE_LSA||mode==MODE_LSB))
294 {
295 string *vrooms;
296 object vcompiler =
297 find_object(implode(patharray[0..<2],"/")+"/virtual_compiler");
298 if (vcompiler && pointerp(vrooms=(mixed *)vcompiler->QueryObjects()))
MG Mud User88f12472016-06-24 23:31:02 +0200299 map(vrooms,#'_vc_map,&data);
Zesstra0f6a3022018-11-07 00:44:32 +0100300 }
301 mixed *files=({});
MG Mud User88f12472016-06-24 23:31:02 +0200302 if (sizeof(data)) // passende files
303 {
304 mixed *subfiles;
305 subfiles=({});
Zesstra0f6a3022018-11-07 00:44:32 +0100306 string path=implode(patharray[0..<2],"/")+"/";
MG Mud User88f12472016-06-24 23:31:02 +0200307 while (sizeof(data))
308 {
309 subfiles=({});
Zesstra0f6a3022018-11-07 00:44:32 +0100310 string base=data[BASENAME];
MG Mud User88f12472016-06-24 23:31:02 +0200311 if (mode==MODE_LSB||(base!="."&&base!=".."))
312 {
313 //DEBUG("PATH: " + path+" BASE: " + base + " MODE: " + mode);
Zesstra0f6a3022018-11-07 00:44:32 +0100314 string full=path+base;
315 string dest2;
316 if (dest=="/" || file_size(dest[0..<2])==FSIZE_DIR)
317 dest2 = dest+base;
318 else
319 dest2 = dest[0..<2];
320
MG Mud User88f12472016-06-24 23:31:02 +0200321 //DEBUG("DEST: " + dest);
Zesstra0f6a3022018-11-07 00:44:32 +0100322 if (recursive && data[FILESIZE]==FSIZE_DIR) // Verzeichnis, Rekursiv
MG Mud User88f12472016-06-24 23:31:02 +0200323 subfiles=_get_files(full+"/",filemask,mode,dest2+"/");
Zesstra0f6a3022018-11-07 00:44:32 +0100324 if (!(filemask && !sizeof(subfiles)
325 && !sizeof(regexp(({ base }),filemask,RE_TRADITIONAL))))
MG Mud User88f12472016-06-24 23:31:02 +0200326 {
Zesstra0f6a3022018-11-07 00:44:32 +0100327 if (!filemask || mode!=MODE_RM)
MG Mud User88f12472016-06-24 23:31:02 +0200328 files+=({ data[0..2]+({ full, path, dest2,sizeof(subfiles)}) });
Zesstra0f6a3022018-11-07 00:44:32 +0100329 if (sizeof(files)+sizeof(subfiles) > MAX_ARRAY_SIZE)
MG Mud User88f12472016-06-24 23:31:02 +0200330 raise_error("Zu viele Files (>3000)!! Abgebrochen!\n");
331 files+=subfiles;
332 }
333 }
Zesstra0524def2018-11-07 22:48:37 +0100334 if (sizeof(data)>3)
335 data=data[3..];
336 else
337 break;
MG Mud User88f12472016-06-24 23:31:02 +0200338 }
339 return files;
340 }
341// File existiert nicht -> Wildcard oder tatsaechlich nicht existent
342// Behandeln je nach mode
343 switch(mode)
344 {
345 case MODE_CP:
346 case MODE_MV:
347 case MODE_CD:
348 case MODE_LSA:
349 files=_get_matching(patharray+(filemask?({ "*" }):({})),1,"/",mode,
350 recursive,dest,filemask);
351 break;
352 default: break;
353 }
354 return files;
355}
356
357
358//
359// file_list(): Liste der Fileinformationen
360// files: Array der Filenamen MIT Wildcards
361// mode: Welche Daten werden benoetigt
362// recursive: Rekursiv listen?
363// dest: Zielverzeichnis
364// mask: Maske (regexp, wenn definiert)
365// Rueckgabe: Liste der betroffenen Files
366//
367
Zesstra0f6a3022018-11-07 00:44:32 +0100368static varargs mixed *file_list(string *files, int mode, int recursive,
369 string dest, string mask)
MG Mud User88f12472016-06-24 23:31:02 +0200370{
Zesstra0f6a3022018-11-07 00:44:32 +0100371 string *list=({});
MG Mud User88f12472016-06-24 23:31:02 +0200372 if (mask) mask=glob2regexp(mask);
Zesstraf22b6a82018-11-07 22:39:55 +0100373 // dest muss einmal normalisiert werden, dann muss es das in den gerufenen
374 // (rekursiven) Funktionen nicht immer nochmal gemacht werden.
375 dest=master()->normalize_path(dest, getuid(RPL||PL), 1);
376
Zesstra0f6a3022018-11-07 00:44:32 +0100377 foreach(string file: files)
MG Mud User88f12472016-06-24 23:31:02 +0200378 {
379 // Abschliessenden / von Pfadnamen abschneiden, weil in diesem Fall
380 // die Inhalte der ersten Unterverzeichnisebene mit ausgegeben
381 // wurden. Ursache hierfuer ist ein Fix an full_path_array() im
382 // Masterobjekt, der dazu fuehrte, dass get_dir() den abschliessenden /
383 // des uebergebenen Pfades jetzt korrekt behandelt. \o/
Zesstra0f6a3022018-11-07 00:44:32 +0100384 if ( sizeof(file) > 1 && file[<1] == '/' )
MG Mud User88f12472016-06-24 23:31:02 +0200385 {
Zesstra0f6a3022018-11-07 00:44:32 +0100386 file = file[0..<2];
387 if ( !sizeof(file) )
MG Mud User88f12472016-06-24 23:31:02 +0200388 continue;
389 }
Zesstra0f6a3022018-11-07 00:44:32 +0100390 string err=catch(list += get_files(file,mode,recursive,dest,mask));
391 if (err)
MG Mud User88f12472016-06-24 23:31:02 +0200392 {
393 printf("Fehler aufgetreten: %s\n",err);
394 log_file(SHELLLOG("FILE_LIST"),
395 sprintf("%s fuehrte folgendes Kommando aus: (Zeit: %s)\n"
396 " >>%s %s<<\n"
397 " Folgender Fehler trat dabei auf:\n"
398 " %s\n\n",
399 capitalize(getuid())||"<Unbekannt>",dtime(time()),
400 query_verb()||"*",_unparsed_args()||"*",err||"*"));
401 return ({});
402 }
403 }
404 return list;
405}