// -*- c++ -*-
/*
* Jakelib2 - General purpose C++ library
* Copyright (C) 2001 Florian Wolff (florian@donuz.de)
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library 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
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*
* $Id: File.jlc,v 1.30 2006-02-11 12:15:15 florian Exp $
*/
#include "jakelib2.h"
#include "jakelib2/io/File.h"
#include "jakelib2/io/FileInfo.h"
#include "jakelib2/io/FilenameFilter.h"
#include "jakelib2/io/FileOutputStream.h"
#include "jakelib2/lang/System.h"
#include "jakelib2/util/ArrayList.h"
#include "jakelib2/util/StringTokenizer.h"
using namespace jakelib::lang;
using namespace jakelib::io;
using namespace jakelib::util;
JAKELIB_IMPLEMENT_CLASS("jakelib.io.File", File, Object);
JAKELIB_IMPLEMENT_ARRAY(File);
#if defined(BORLAND)
# include
# include
#elif defined(JAKELIB_WIN32API)
# include
# include
#else
# include
# include
# include
#endif
#ifdef HAVE_UNISTD_H
# include
#endif
#ifdef WIN32
char File::separatorChar = '\\';
#else
char File::separatorChar = '/';
#endif
String *File::separator = null;
String *File::pathSeparator = null;
char File::pathSeparatorChar = ':';
String *File::tempDir = null;
#ifdef POSIX
Files *File::roots = null;
#endif
/*****************************************************************************\
* File |
*****************************************************************************/
File::File(String* pathname)
{
init(pathname);
}
File::File(char* pathname)
{
init(new String(pathname));
}
File::File(File* parent, String* child)
{
if (parent == null) {
init(child);
} else {
if (parent->getPath()->endsWith(separator)) {
init(parent->getPath() .. child);
} else {
init(parent->getPath() .. separator .. child);
}
}
}
File::File(String* parent, String* child)
{
if (parent == null) {
init(child);
} else {
if (parent->endsWith(separator)) {
init(parent .. child);
} else {
init(parent .. separator .. child);
}
}
}
/*****************************************************************************\
* init |
*****************************************************************************/
void File::init(String* pathname)
{
path = canonicalFilename(pathname);
update();
}
/*****************************************************************************\
* list |
*****************************************************************************/
ArrayList* File::list(FilenameFilter *filter)
{
if (!isDirectory())
return null;
ArrayList* list = null;
String* pattern;
if (path->length() > 0 && !path->endsWith(separator))
pattern = path->plus(separator);
else
pattern = path;
#if defined(BORLAND)
struct ffblk dir;
pattern.append("*");
int done = findfirst(pattern->latin1(), &dir, FA_SYSTEM|FA_DIREC|FA_RDONLY|FA_HIDDEN);
//printf("Pattern: \"%s\"\n", pattern.latin1());
if (done != 0) {
return null;
}
else {
list = new ArrayList();
while (!done) {
String *f = new String(dir.ff_name);
if (filter == null || filter->accept(null, f))
list->add(f);
done = findnext(&dir);
}
}
return list;
#elif defined(JAKELIB_WIN32API)
HANDLE dir;
WIN32_FIND_DATA dirent;
if ((dir = FindFirstFile(pattern->plus("*")->latin1(), &dirent)) != INVALID_HANDLE_VALUE) {
list = new ArrayList();
do {
String *f = new String(dirent.cFileName);
if (filter == null || filter->accept(null, f))
list->add(f);
}
while(FindNextFile(dir, &dirent));
FindClose(dir);
return list;
}
else {
return null;
}
#else
DIR* dir = opendir(pattern->latin1());
if (dir == null) {
return null;
}
else {
list = new ArrayList();
struct dirent* entry;
while ((entry = readdir(dir)) != null) {
String* f = new String(entry->d_name);
if (filter == null || filter->accept(null, f))
list->add(f);
}
closedir(dir);
}
return list;
#endif
}
/*****************************************************************************\
* isDirectory |
*****************************************************************************/
jboolean File::isDirectory()
{
return getInfo()->isDirectory();
}
/*****************************************************************************\
* isFile |
*****************************************************************************/
jboolean File::isFile()
{
return getInfo()->isFile();
}
/*****************************************************************************\
* isLink |
*****************************************************************************/
jboolean File::isLink()
{
return getInfo()->isLink();
}
/*****************************************************************************\
* isSystem |
*****************************************************************************/
jboolean File::isSystem()
{
return getInfo()->isSystem();
}
/*****************************************************************************\
* isHidden |
*****************************************************************************/
jboolean File::isHidden()
{
return getInfo()->isHidden();
}
/*****************************************************************************\
* update |
*****************************************************************************/
void File::update()
{
if (path->length() == 0) {
ext = String::emptyString;
name = String::emptyString;
fname = String::emptyString;
dir = String::emptyString;
} else {
// Split directory and file name:
int slashPos = path->lastIndexOf(separator);
if (slashPos != -1) {
name = path->substring(slashPos + 1);
dir = path->substring(0, slashPos +1);
}
else {
name = path;
dir = String::emptyString;
}
// Extract extension:
int dotPos = name->lastIndexOf(".");
if (dotPos != -1) {
ext = name->substring(dotPos);
fname = name->substring(0, dotPos);
}
else {
ext = String::emptyString;
fname = name;
}
}
}
/*****************************************************************************\
* length |
*****************************************************************************/
jlong File::length()
{
return getInfo()->length();
}
/*****************************************************************************\
* lastModified |
*****************************************************************************/
jlong File::lastModified()
{
return getInfo()->lastModified();
}
/*****************************************************************************\
* readlink |
*****************************************************************************/
String* File::readlink()
{
return getInfo()->readlink();
}
/*****************************************************************************\
* getTempDir |
*****************************************************************************/
String* File::getTempDir()
{
if (tempDir == null) {
#ifdef WIN32
throw new UnsupportedOperationException(`"getTempDir is not yet supported under WIN32"`
.. JAKELIB_AT2(`"jakelib.io.File.getTempDir"`));
#else
tempDir = new String("/tmp");
#endif
}
return tempDir;
}
/*****************************************************************************\
* createTempFile |
*****************************************************************************/
File* File::createTempFile(char* prefix, char* suffix)
{
return createTempFile(prefix, suffix, null);
}
File* File::createTempFile(char* prefix, char* suffix, char* directory)
{
String suff;
String* directoryName;
if (prefix == null)
throw new NullPointerException(`"Prefix must not be null"`
.. JAKELIB_AT2(`"jakelib.io.File.createTempFile"`));
if (suffix == null)
suff = ".tmp";
else
suff = suffix;
if (directory == null)
directoryName = getTempDir();
else
directoryName = new String(directory);
String* templ = directoryName;
if (directoryName->length() > 0 && !directoryName->endsWith(separator))
templ = templ->plus(separator);
templ = templ->plus(prefix)->plus("XXXXXX");
char* templPtr = (char*) templ->latin1();
#if !defined(JAKELIB_WIN32API)
int file = mkstemp(templPtr);
if (file == -1) {
throw new IOException(`"Unable to create temp file"`
.. JAKELIB_AT2(`"jakelib.io.File.createTempFile"`));
}
::close(file);
#else
//GetTempFileName()
throw new UnsupportedOperationException(`"File.createTempFile is not yet supported for Borland."`
.. JAKELIB_AT2(`"jakelib.io.File.createTempFile"`));
#endif
String filename(templPtr);
return new File(&filename);
}
/*****************************************************************************\
* remove |
*****************************************************************************/
jboolean File::remove()
{
#if defined(JAKELIB_WIN32API)
if (isFile()) {
return (DeleteFile(path->latin1()) != 0);
}
else if (isDirectory()) {
return (RemoveDirectory(path->latin1()) != 0);
}
#else
if (isFile() || isLink()) {
return (::remove(path->latin1()) == 0);
} else {
return (::rmdir(path->latin1()) == 0);
}
#endif
return false;
}
/*****************************************************************************\
* purge |
*****************************************************************************/
jboolean File::purge()
{
FileInfo* fi = getInfo();
if (fi->isDirectory() && !fi->isLink()) {
List* children = list();
if (children != null) {
for (int idx = 0; idx < children->size(); idx++) {
String* name = (String*) children->get(idx);
if (!name->equals(`".."`) && !name->equals(`"."`)) {
File* f = new File(this, name);
f->purge();
}
}
}
}
return remove();
}
/*****************************************************************************\
* initFileClass |
*****************************************************************************/
void File::initFileClass()
{
#ifdef POSIX
separator = new String("/");
pathSeparator = new String(":");
#else
separator = new String("\\");
pathSeparator = new String(":");
#endif
}
/*****************************************************************************\
* init2FileClass |
*****************************************************************************/
void File::init2FileClass()
{
#ifdef POSIX
roots = new Files(1);
roots->set(0, new File(`"/"`));
#else
#endif
}
/*****************************************************************************\
* canonicalFilename |
*****************************************************************************/
String* File::canonicalFilename(String *filename)
{
#ifdef POSIX
return filename->replace('\\', '/');
#else
return filename->replace('/', '\\');
#endif
}
/*****************************************************************************\
* listRoots |
*****************************************************************************/
Files* File::listRoots()
{
#ifdef POSIX
return roots;
#else
ArrayList driveNames;
ULONG driveMask = _getdrives();
char drvName[] = "A:\\";
while (driveMask) {
if (driveMask & 1) {
driveNames.add(new String(drvName));
}
drvName[0]++;
driveMask >>= 1;
}
Files *roots = new Files(driveNames.size());
for (int idx = 0; idx < driveNames.size(); idx++)
roots->set(idx, new File((String*) driveNames.get(idx)));
return roots;
#endif
}
/*****************************************************************************\
* getAbsolutePath |
*****************************************************************************/
String* File::getAbsolutePath()
{
if (isAbsolute()) {
return path;
}
char cwd[JAKELIB_MAX_PATH];
#ifdef POSIX
if (getcwd(cwd, JAKELIB_MAX_PATH) == null)
return path;
#else
if (GetCurrentDirectory(JAKELIB_MAX_PATH, cwd) == 0)
return path;
#endif
String *dir = new String(cwd);
if (path->length() == 0)
return dir;
if (dir->endsWith(separator) || path->startsWith(separator))
return dir .. path;
else
return dir .. separator .. path;
}
/*****************************************************************************\
* getCanonicalPath |
*****************************************************************************/
String* File::getCanonicalPath()
{
String *absPath = getAbsolutePath();
StringBuffer buf(absPath->length());
#ifdef POSIX
String *prefix = `""`;
absPath = absPath->substring(1);
#else
String *prefix = absPath->substring(0, 2);
absPath = absPath->substring(3);
#endif
StringTokenizer tokenizer(absPath, separator);
int lastSlash = -1;
while (tokenizer.hasMoreTokens()) {
String *part = tokenizer.nextToken();
if (`".."`->equals(part)) {
int slashPos = buf.toString()->lastIndexOf(separator);
if (slashPos > -1) {
buf.setLength(slashPos);
}
}
else if (part->length() != 0 && !`"."`->equals(part)) {
buf.append(separator);
buf.append(part);
}
}
if (buf.length() == 0) {
buf.append(File::separator);
}
return prefix .. buf.toString();
}
/*****************************************************************************\
* isAbsolute |
*****************************************************************************/
jboolean File::isAbsolute()
{
#ifdef POSIX
return (path->startsWith(separator));
#else
return (path->length() >= 3
&& path->charAt(1) == ':'
&& path->charAt(2) == '\\');
#endif
}
/*****************************************************************************\
* mkdir |
*****************************************************************************/
jboolean File::mkdir()
{
#ifdef POSIX
return (::mkdir(path->latin1(), S_IRUSR|S_IWUSR|S_IXUSR | S_IRGRP|S_IXGRP | S_IROTH|S_IXOTH) == 0);
#else
// TODO: mkdir under Windows
return false;
#endif
}
/*****************************************************************************\
* touch |
*****************************************************************************/
void File::touch()
{
#ifdef POSIX
if (isFile()) {
utime(path->latin1(), null);
} else {
FileOutputStream f(this);
f.close();
}
#else
// TODO: touch under Windows
#endif
}
/*****************************************************************************\
* rename |
*****************************************************************************/
jboolean File::rename(String* newName)
{
return (::rename(path->latin1(), newName->latin1()) == 0);
}
/*****************************************************************************\
* getParent |
*****************************************************************************/
File* File::getParent()
{
String* dir = getDir();
if (dir->endsWith(separator)) {
return new File(dir->substring(0, dir->length() -1));
} else {
return new File(dir);
}
}
/*****************************************************************************\
* getInfo |
*****************************************************************************/
FileInfo* File::getInfo()
{
return new FileInfo(this);
}