1
// This file is part of PUMA.
2
// Copyright (C) 1999-2003 The PUMA developer team.
4
// This program is free software; you can redistribute it and/or
5
// modify it under the terms of the GNU General Public License as
6
// published by the Free Software Foundation; either version 2 of
7
// the License, or (at your option) any later version.
9
// This program is distributed in the hope that it will be useful,
10
// but WITHOUT ANY WARRANTY; without even the implied warranty of
11
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12
// GNU General Public License for more details.
14
// You should have received a copy of the GNU General Public
15
// License along with this program; if not, write to the Free
16
// Software Foundation, Inc., 59 Temple Place, Suite 330, Boston,
19
#include "Puma/Config.h"
20
#include "Puma/PathIterator.h"
21
#include "Puma/ErrorStream.h"
22
#include "Puma/PathManager.h"
23
#include "Puma/FileUnit.h"
24
#include "Puma/MacroUnit.h"
25
#include "Puma/RegComp.h"
26
#include "Puma/SysCall.h"
30
#define PATH_MAX _MAX_PATH
36
PathManager::~PathManager () {
37
for (int i = numProts () - 1; i >= 0; i--)
42
// Join the paths of the given manager with the paths
43
// of this path manager.
44
void PathManager::join (PathManager &pm) {
45
for (int i = 0; i < pm.numPaths (); i++)
46
addPath (pm.src (i), pm.dest (i));
47
for (int i = 0; i < pm.numProts (); i++)
48
_protected.append (new RegComp (*pm.prot (i)));
52
// Add the separator '/' to the copy of the given string.
53
char *PathManager::addSeparator (const char *s) const {
54
if (! s) return (char*) 0;
57
char *str = new char[pos + 2];
59
if (str[pos - 1] != '/') // Add a trailing '/'.
65
// Add a new source directory.
66
void PathManager::addPath (const char *source, const char *destination) {
68
char *newsrc = addSeparator (source);
70
// Don't add a source directory twice.
71
// Don't add a sub-directory of an existing source directory.
72
for (int pos = numPaths () - 1; pos >= 0; pos--)
73
if (strncmp (src (pos), newsrc, strlen (src (pos))) == 0) {
78
// Add the source path.
79
_paths[numPaths ()].src (newsrc);
82
// Add the destination path.
83
char *newdest = addSeparator (destination);
84
_paths[numPaths () - 1].dest (newdest);
88
// add the files to iterator file list
93
else if (destination) {
94
// Add the destination path.
95
char *newdest = addSeparator (destination);
96
_paths[numPaths ()].dest (newdest);
98
_paths[numPaths () - 1].src (0);
103
// find the canonical filename representation for a file
104
bool PathManager::canonFilename (Filename name, Filename &abs_name) const {
106
string file_abs = "";
107
const char *path = name.path ();
108
if (strcmp (path, "") == 0)
111
Filename canon_file, canon_dir;
112
if (SysCall::canonical (name, canon_file))
113
file_abs = canon_file.name ();
114
else if (SysCall::canonical (name.path (), canon_dir)) {
115
file_abs = canon_dir.name ();
116
const char *nodir = strrchr (name.name (), '/');
117
if (!nodir) nodir = strrchr (name.name (), '\\');
122
return false; // neither file nor directory exist
124
abs_name.name (file_abs.c_str ());
129
// checks if a give file (by name) is a registered file of this path manager
130
// In case of success (found!) the an iterator is returned, which can be
131
// used to access more information about the file.
132
bool PathManager::isBelow (const char *file, PFMConstIter &iter) const {
134
// determine the canonical name (which has to exist)
136
if (!canonFilename (file, file_abs))
139
// search for the name and return the result
140
iter = _files.find (string (file_abs.name ()));
141
return iter != _files.end ();
145
// Add a new file to the project file list
146
PFMConstIter PathManager::addFile (const ProjectFile &file) {
149
bool have_canon = canonFilename (file.name (), file_abs);
152
return _files.end ();
155
// insert the file with its canonical name as the key
156
pair<PFMConstIter, bool> insert_result =
157
_files.insert (PFMPair (string (file_abs.name ()), file));
159
// return the iterator
160
return insert_result.first;
164
// Add a new file to the project file list
165
PFMConstIter PathManager::addFile (Filename file) {
166
// determine the destination path and call addFile with two args
167
std::ostringstream path;
168
bool have_dest = getDestinationPath (file.name (), path);
170
string dest_path = path.str ();
171
return addFile (file, have_dest ? dest_path.c_str () : "<unused-dest-name>");
175
// Add a new file to the project file list with destination path
176
PFMConstIter PathManager::addFile (Filename name, Filename dest) {
177
return addFile (ProjectFile (name, dest));
181
// Set the destination directory of the given source directory.
182
void PathManager::setDest (const char *source, const char *destination) {
183
if (! source) return;
185
// Search the corresponding path info object.
187
for (pos = numPaths () - 1; pos >= 0; pos--)
188
if (strcmp (src (pos), source) == 0)
190
if (pos < 0) return; // Source path doesn't exist.
192
// Set the destination path.
193
char *newdest = addSeparator (destination);
194
_paths[pos].dest (newdest);
197
_paths[pos].dest (destination);
201
// Configure the project from the command line or a file.
202
void PathManager::configure (const Config &c) {
203
const ConfOption *d = 0, *p = 0;
205
for (unsigned i = 0; i < c.Options (); i++) {
206
const ConfOption *o = c.Option (i);
207
bool new_p = false, new_d = false;
209
if (! strcmp (o->Name (), "-w")) {
210
if (o->Arguments () != 1) continue;
211
protect (o->Argument (0));
212
} else if (! strcmp (o->Name (), "-p")) {
213
if (o->Arguments () != 1) continue;
215
} else if (! strcmp (o->Name (), "-d")) {
216
if (o->Arguments () != 1) continue;
222
addPath (p->Argument (0), d ? d->Argument (0): 0);
230
addPath (p ? p->Argument (0) : 0, d->Argument (0));
238
addPath (p ? p->Argument (0) : 0, d ? d->Argument (0) : 0);
242
// Initial globbing implementation.
243
void PathManager::glob (char *pattern) {
244
// Explore the source directories of the manager and for
245
// any file matching the given pattern call the method
246
// action(filename). The default pattern is ".*".
247
PathIterator iter (pattern);
248
while (iterate (iter))
253
// Iterate the contents of the paths.
254
const char *PathManager::iterate (PathIterator &iter) const {
256
// a new iterator should start at the beginning
257
if (!iter.in_use ()) {
258
iter.init (_files.begin ());
263
// search for the next matching element
264
while (iter != _files.end () &&
265
! iter._regexp->match (iter->second.name ().name ())) {
270
if (iter == _files.end ()) {
275
return iter->second.name ().name ();
280
// find all files of a directory tree and add them to _files
281
void PathManager::traverse (const char *path) {
282
// open the current directory
283
DirHandle dp = SysCall::opendir (path, &err ());
284
if (! dp) { // Skip the directory, may be an access problem.
285
err () << sev_error << "Couldn't open directory `"
286
<< path << "'." << endMessage;
290
// Read the current directory entries.
292
while ((entry = SysCall::readdir (dp, &err()))) {
293
// Create the full name of the current entry, e.g. /tmp/file.c.
294
char *name = new char[strlen (path) + strlen (entry) + 2];
296
if (name[strlen (name) - 1] != '/')
298
strcat (name, entry);
300
// Read the attributes of the current entry.
302
if (! SysCall::stat (name, fi, &err())) continue;
304
// Test whether entry is a directory.
306
traverse (name); // Dive into the sub-directory.
313
// Close the current directory and go one up.
314
SysCall::closedir (dp, &err());
318
// Add a regular pattern specifying a path that has to be
319
// protected from writing.
320
void PathManager::protect (const char *path) {
322
_protected.append (new RegComp (path));
326
// Return true if the given file or path is protected
327
// from writing or if it isn't located within one of the
328
// source directories, because then it's protected, too.
329
bool PathManager::isProtected (const char *file) const {
330
if (! file) return false;
332
// Protected by protect patterns?
333
for (int i = numProts () - 1; i >= 0; i--) {
334
if (prot (i)->match (file))
338
// From outside the source directories?
339
return ! isBelow (file);
343
const char *PathManager::getDestination (Filename file, ostream *name) const {
345
// determine the canonical name (which has to exist)
347
if (!canonFilename (file, file_abs))
350
for (int i = numPaths () - 1; i >= 0; i--) {
351
// TODO: don't determine the canonical directory names every time
353
if (!canonFilename (src (i), dir_abs)) {
357
// is the canonical dir name a prefix of the filename?
358
int len = strlen (dir_abs.name ());
359
if (strncmp (dir_abs.name (), file_abs.name (), len) == 0 &&
360
*(file_abs.name () + len) == '/') {
362
*name << file_abs.name () + len + 1;
370
bool PathManager::getDestinationPath (const char *filename, ostream &out) const {
371
std::ostringstream rest;
372
const char *dir = getDestination (filename, &rest);
376
if (dir [strlen (dir) - 1] != '/')
378
out << rest.str ().c_str ();