/* -*-Mode: C++;-*-
* PRCS - The Project Revision Control System
* Copyright (C) 1997 Josh MacDonald
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*
* $Id: populate.cc 1.11.1.6.1.12.1.3.1.8.1.22 Wed, 24 Oct 2001 03:23:43 -0700 jmacd $
*/
#include "prcs.h"
#include "prcsdir.h"
#include "projdesc.h"
#include "hash.h"
#include "repository.h"
#include "misc.h"
#include "fileent.h"
#include "system.h"
#include "checkin.h"
#include "populate.h"
static int added = 0;
static int deleted = 0;
static void append_new_files(ProjectDescriptor* P,
FileRecordList *record_list,
bool delete_files);
static int hash_dir(const char*, InoTable*, PathTable*);
static int hash_file_or_dir(const char*, InoTable*, PathTable*);
static PrVoidError check_project_file_populate(ProjectDescriptor *,
PathTable *,
InoTable *);
static PrVoidError init_ignore(ProjectDescriptor* project);
static bool not_ignored(const char* name);
PrPrcsExitStatusError populate_command_filename(const char* filename, bool verbose);
static FileRecordList *new_records = NULL;
static FileRecordList *deleted_records = NULL;
static VoidPtrList *ignore = NULL;
PrPrcsExitStatusError populate_command()
{
return populate_command_filename(cmd_root_project_file_path, true);
}
PrPrcsExitStatusError populate_command_filename(const char* filename, bool verbose)
{
ProjectDescriptor *P;
PathTable table(pathname_hash, pathname_equal),
descr(pathname_hash, pathname_equal);
InoTable inodes;
Return_if_fail(P << read_project_file(cmd_root_project_full_name,
cmd_root_project_file_path,
true,
KeepNothing));
Return_if_fail(check_project_file_populate(P, &table, &inodes));
Return_if_fail(init_ignore(P));
if(cmd_filenames_count < 1)
hash_dir(cmd_root_project_path[0] ? cmd_root_project_path : ".", &inodes, &table);
else {
/* the functionality here is the same as eliminate_unnamed_files
* from checkin.cc, but its not really eliminating, rather its
* marking in this case. */
for(int i = 0; i < cmd_filenames_count; i += 1) {
if (weird_pathname(cmd_corrected_filenames_given[i] + cmd_root_project_path_len))
pthrow prcserror << "Illegal file name "
<< squote(cmd_corrected_filenames_given[i])
<< "names may not contain "
<< squote("../") << " or " << squote("./")
<< "or end or begin with " << squote("/")
<< dotendl;
else
hash_file_or_dir(cmd_corrected_filenames_given[i], &inodes, &table);
}
}
if(added == 0 && deleted == 0) {
prcsinfo << "No new files" << dotendl;
return ExitSuccess;
}
if(verbose && option_long_format)
prcsinfo << "New files are: " << prcsendl;
if(added > 0) {
P->append_files_data ("\n;; Files added by populate at ");
P->append_files_data (get_utc_time());
P->append_files_data (",\n;; to version ");
P->append_files_data (P->full_version());
P->append_files_data (", by ");
P->append_files_data (get_login());
P->append_files_data (":\n");
append_new_files(P, new_records, false);
P->append_files_data ("\n");
}
if(deleted > 0) {
P->append_files_data ("\n;; Files deleted by populate at ");
P->append_files_data (get_utc_time());
P->append_files_data (",\n;; from version ");
P->append_files_data (P->full_version());
P->append_files_data (", by ");
P->append_files_data (get_login());
P->append_files_data (":\n");
append_new_files(P, deleted_records, true);
P->append_files_data ("\n");
}
if(verbose) {
if(added == 1) {
prcsinfo << "One file was added" << dotendl;
} else if(added == 0) {
prcsinfo << "No new files" << dotendl;
} else {
prcsinfo << added << " files were added" << dotendl;
}
if(option_populate_delete) {
if(deleted == 1) {
prcsinfo << "One file was deleted" << dotendl;
} else if(deleted == 0) {
prcsinfo << "No files deleted" << dotendl;
} else {
prcsinfo << deleted << " files were deleted" << dotendl;
}
}
}
if(option_report_actions)
return ExitSuccess;
Return_if_fail(P->write_project_file(filename));
return ExitSuccess;
}
static int hash_file_or_dir(const char* name, InoTable* T, PathTable* P)
{
struct stat statbuf;
Dstring *n;
FileRecord r;
n = new Dstring(name);
r.name = n;
if ( lstat(name, &statbuf) < 0 ) {
delete n;
return 0;
} else if ( S_ISREG(statbuf.st_mode) ) {
/* first look in the table for this inode */
FileType *lu(T->lookup(statbuf.st_ino));
if ( lu == NULL && not_ignored(*n) ) { /* if not found then its new */
r.type = RealFile; /* set its type */
T->insert(statbuf.st_ino, RealFile); /* insert it */
P->insert(*n, RealFile);
new_records = new FileRecordList(r, new_records);
added += 1;
}
} else if ( S_ISLNK(statbuf.st_mode) ) {
FileType *lu(P->lookup(*n));
if ( lu == NULL && not_ignored(*n) ) {
r.type = SymLink;
P->insert(*n, SymLink);
new_records = new FileRecordList(r, new_records);
added += 1;
}
} else if ( S_ISDIR(statbuf.st_mode) ) {
Dstring tmp(name);
tmp.append('/');
int inthisdir = hash_dir(tmp, T, P);
FileType *lu(T->lookup(statbuf.st_ino));
if ( inthisdir == 0 && lu == NULL && not_ignored(*n)) {
r.type = Directory;
T->insert(statbuf.st_ino, Directory);
P->insert(*n, Directory);
new_records = new FileRecordList(r, new_records);
added += 1;
}
} else {
prcswarning << "Ignoring special file: " << squote(name)
<< ", continuing" << dotendl;
delete n;
return 0;
}
return 1;
}
static int hash_dir(const char* dir, InoTable* T, PathTable *P)
{
Dstring d = dir;
int len, total = 0;
if(strcmp(d, ".") == 0)
d.truncate(0);
len = d.length();
Dir current_dir(dir);
foreach(ent_ptr, current_dir, Dir::DirIterator) {
d.truncate(len);
d.append(*ent_ptr);
total += hash_file_or_dir(d, T, P);
}
if (!current_dir.OK())
prcserror << "Error reading directory " << squote(dir) << perror;
return total;
}
static bool heuristic_keyword_guess(const char* path)
{
if (option_nokeywords) {
return true;
}
FILE* file;
char buffer[1024];
int nread = 0;
If_fail(file << Err_fopen(path, "r"))
return false;
If_fail (nread << Err_fread (buffer, 1024, file))
nread = -1;
fclose(file);
if (nread < 0)
return false;
else
return memchr(buffer, 0, nread) != NULL;
}
static void append_new_files(ProjectDescriptor* P,
FileRecordList *record_list,
bool delete_files)
{
const char *name;
FileType ft;
FileEntry* fe;
for (; record_list; record_list = record_list->tail()) {
ft = record_list->head().type;
name = *record_list->head().name + cmd_root_project_path_len;
fe = record_list->head().fe;
if(option_long_format) {
if(delete_files) {
prcsoutput << "Deleted " << squote(name) << " of type "
<< format_type(ft) << prcsendl;
} else {
prcsoutput << "Added " << squote(name) << " of type "
<< format_type(ft) << prcsendl;
}
}
if(delete_files) {
P->append_file_deletion (fe);
} else if (ft == SymLink) {
const char* ln;
If_fail (ln << read_sym_link (name))
ln = "";
P->append_link (name, ln);
} else if (ft == Directory) {
P->append_directory (name);
} else if (ft == RealFile) {
P->append_file (name, heuristic_keyword_guess(name));
}
}
}
PrVoidError check_project_file_populate(ProjectDescriptor *P,
PathTable *table,
InoTable *inodes)
{
FileType type;
const char* name;
foreach_fileent(fe_ptr, P) {
FileEntry *fe = *fe_ptr;
type = fe->file_type();
name = fe->working_path();
bool file_present;
if(Failure(file_present << fe->check_working_file()) || !file_present) {
if(option_populate_delete && fe->on_command_line()) {
char c;
static BangFlag bang;
prcsquery << "File " << squote(name) << " is unavailable. "
<< force("Deleting")
<< report("Delete")
<< allow_bang(bang)
<< option('n', "Don't delete this file")
<< defopt('y', "Delete from project file")
<< query("Delete");
Return_if_fail(c << prcsquery.result());
if(c == 'y') {
deleted += 1;
deleted_records = new FileRecordList(
FileRecord(fe->file_type(),
new Dstring(fe->working_path()),
fe),
deleted_records);
P->delete_file(fe);
}
}
table->insert(name, type);
continue;
}
table->insert(name, type);
inodes->insert(fe->stat_inode(), type);
}
struct stat buf;
If_fail (Err_stat (P->project_file_path(), &buf))
pthrow prcserror << "Stat failed on file " << squote (P->project_file_path()) << perror;
inodes->insert (buf.st_ino, RealFile);
If_fail (Err_stat (P->project_aux_path(), &buf)) {
} else {
inodes->insert (buf.st_ino, RealFile);
}
return NoError;
}
static PrVoidError init_ignore(ProjectDescriptor* project)
{
OrderedStringTable *ignore_array = project->populate_ignore();
foreach (ds_ptr, ignore_array->key_array(), OrderedStringTable::KeyArray::ArrayIterator) {
const char* ds = (*ds_ptr);
reg2ex2_t *r = new reg2ex2_t;
Return_if_fail (prcs_compile_regex (ds, r));
ignore = new VoidPtrList (r, ignore);
}
return NoError;
}
static bool not_ignored(const char* name)
{
VoidPtrList *i = ignore;
for (; i; i = i->tail()) {
bool matches = prcs_regex_matches(name, (reg2ex2_t*) i->head());
if(matches) {
if (option_long_format)
prcsoutput << "Ignoring file " << squote(name) << dotendl;
return false;
}
}
return true;
}
PrVoidError prcs_compile_regex(const char* pat, reg2ex2_t *r)
{
if(reg2comp (r, pat, REG2_NOSUB) != 0)
pthrow prcserror << "Regular expression compilation failed on "
<< pat << dotendl;
/* Why's the error interface have to be so difficult? */
return NoError;
}
bool prcs_regex_matches(const char* name, reg2ex2_t* r)
{
return reg2ex2ec(r, name, 0, 0, 0) == 0;
}
/**********************************************************************/
/* Depopulate */
/**********************************************************************/
PrPrcsExitStatusError depopulate_command()
{
ProjectDescriptor *project;
int files = 0;
bool once = true;
Return_if_fail(project << read_project_file(cmd_root_project_full_name,
cmd_root_project_file_path,
true,
KeepNothing));
eliminate_unnamed_files(project);
if (cmd_filenames_count == 0) {
/* Do they really want to do this? */
prcsquery << "You have requested to delete every file in the working project. "
<< report ("Continue")
<< force ("Continue")
<< defopt ('y', "Continue")
<< optfail ('n')
<< query ("Are you sure");
Return_if_fail (prcsquery.result());
}
foreach_fileent (fe_ptr, project) {
FileEntry *fe = *fe_ptr;
if (fe->on_command_line()) {
if (option_long_format)
prcsoutput << "Removed file " << squote (fe->working_path()) << dotendl;
if (once) {
once = false;
project->append_files_data ("\n;; Files deleted by depopulate at ");
project->append_files_data (get_utc_time());
project->append_files_data (",\n;; from version ");
project->append_files_data (project->full_version());
project->append_files_data (", by ");
project->append_files_data (get_login());
project->append_files_data (":\n");
}
project->delete_file (fe);
project->append_file_deletion (fe);
files += 1;
}
}
if (!once)
project->append_files_data ("\n");
if (files == 0)
prcsoutput << "Removed no files" << dotendl;
else if (files == 1)
prcsoutput << "Removed 1 file" << dotendl;
else
prcsoutput << "Removed " << files << " files" << dotendl;
if(option_report_actions)
return ExitSuccess;
Return_if_fail(project->write_project_file(project->project_file_path()));
return ExitSuccess;
}
syntax highlighted by Code2HTML, v. 0.9.1