/*
filetools.C (former paths.C) - part of LyX project
- General path-mangling functions
+ General path-mangling functions
Copyright 1996 Ivan Schreter
Parts Copyright 1996 Dirk Niggemann
- Parts Copyright 1985, 1990, 1993 Free Software Foundation, Inc.
+ Parts Copyright 1985, 1990, 1993 Free Software Foundation, Inc.
Parts Copyright 1996 Asger Alstrup
-
+
See also filetools.h.
lyx-filetool.C : tools functions for file/path handling
#include <config.h>
-#include <cctype>
-
-#include <utility>
-#include <fstream>
-
-#include "Lsstream.h"
-
#ifdef __GNUG__
#pragma implementation "filetools.h"
#endif
-#include <cstdlib>
-#include <cstdio>
-#include <fcntl.h>
-#include <cerrno>
#include "debug.h"
#include "support/lstrings.h"
+#include "support/systemcall.h"
#include "filetools.h"
-#include "LSubstring.h"
+#include "lstrings.h"
#include "frontends/Alert.h"
#include "FileInfo.h"
#include "support/path.h" // I know it's OS/2 specific (SMiyata)
#include "lyxlib.h"
#include "os.h"
+#include "Lsstream.h"
+
+#include <cctype>
+#include <cstdlib>
+#include <cstdio>
+#include <fcntl.h>
+#include <cerrno>
+
+#include <utility>
+#include <fstream>
+
+
// Which part of this is still necessary? (JMarc).
#if HAVE_DIRENT_H
# include <dirent.h>
# endif
#endif
+#ifndef CXX_GLOBAL_CSTD
+using std::fgetc;
+using std::isalpha;
+using std::isalnum;
+#endif
+
using std::make_pair;
using std::pair;
using std::endl;
using std::ifstream;
using std::vector;
+using std::getline;
extern string system_lyxdir;
extern string build_lyxdir;
{
string name = OnlyFilename(file);
string const path = OnlyPath(file);
-
+
for (string::size_type i = 0; i < name.length(); ++i) {
name[i] &= 0x7f; // set 8th bit to 0
};
string const keep("abcdefghijklmnopqrstuvwxyz"
"ABCDEFGHIJKLMNOPQRSTUVWXYZ"
"@!\"'()*+,-./0123456789:;<=>?[]`|");
-
+
string::size_type pos = 0;
while ((pos = name.find_first_not_of(keep, pos)) != string::npos) {
name[pos++] = '_';
// Is a file read_only?
// return 1 read-write
// 0 read_only
-// -1 error (doesn't exist, no access, anything else)
+// -1 error (doesn't exist, no access, anything else)
int IsFileWriteable (string const & path)
{
FileInfo fi(path);
-
+
if (fi.access(FileInfo::wperm|FileInfo::rperm)) // read-write
return 1;
if (fi.readable()) // read-only
bool IsDirWriteable (string const & path)
{
lyxerr[Debug::FILES] << "IsDirWriteable: " << path << endl;
-
+
string const tmpfl(lyx::tempName(path, "lyxwritetest"));
if (tmpfl.empty())
return false;
-
+
lyx::unlink(tmpfl);
return true;
}
// If path entry begins with $$LyX/, use system_lyxdir
// If path entry begins with $$User/, use user_lyxdir
// Example: "$$User/doc;$$LyX/doc"
-string const FileOpenSearch (string const & path, string const & name,
+string const FileOpenSearch (string const & path, string const & name,
string const & ext)
{
string real_file, path_element;
bool notfound = true;
string tmppath = split(path, path_element, ';');
-
+
while (notfound && !path_element.empty()) {
path_element = os::slashify_path(path_element);
if (!suffixIs(path_element, '/'))
path_element+= '/';
path_element = subst(path_element, "$$LyX", system_lyxdir);
path_element = subst(path_element, "$$User", user_lyxdir);
-
+
real_file = FileSearch(path_element, name, ext);
-
+
if (real_file.empty()) {
do {
tmppath = split(tmppath, path_element, ';');
vector<string> dirlist;
DIR * dirp = ::opendir(dir.c_str());
if (!dirp) {
- lyxerr[Debug::FILES]
+ lyxerr[Debug::FILES]
<< "Directory \"" << dir
<< "\" does not exist to DirList." << endl;
return dirlist;
}
-
+
dirent * dire;
while ((dire = ::readdir(dirp))) {
string const fil = dire->d_name;
vector<string> dirlist;
directory_iterator dit("dir");
while (dit != directory_iterator()) {
- string fil = dit->filename;
+ string fil = dit->filename;
if (prefixIs(fil, extension)) {
- dirlist.push_back(fil);
+ dirlist.push_back(fil);
}
++dit;
}
// Returns the real name of file name in directory path, with optional
-// extension ext.
-string const FileSearch(string const & path, string const & name,
+// extension ext.
+string const FileSearch(string const & path, string const & name,
string const & ext)
{
// if `name' is an absolute path, we ignore the setting of `path'
// search first without extension, then with it.
if (IsFileReadable(fullname))
return fullname;
- else if (ext.empty())
+ else if (ext.empty())
return string();
else { // Is it not more reasonable to use ChangeExtension()? (SMiyata)
fullname += '.';
fullname += ext;
if (IsFileReadable(fullname))
return fullname;
- else
+ else
return string();
}
}
// 1) user_lyxdir
// 2) build_lyxdir (if not empty)
// 3) system_lyxdir
-string const LibFileSearch(string const & dir, string const & name,
+string const LibFileSearch(string const & dir, string const & name,
string const & ext)
{
- string fullname = FileSearch(AddPath(user_lyxdir, dir), name, ext);
+ string fullname = FileSearch(AddPath(user_lyxdir, dir), name, ext);
if (!fullname.empty())
return fullname;
-
- if (!build_lyxdir.empty())
+
+ if (!build_lyxdir.empty())
fullname = FileSearch(AddPath(build_lyxdir, dir), name, ext);
if (!fullname.empty())
return fullname;
-
+
return FileSearch(AddPath(system_lyxdir, dir), name, ext);
}
string const
-i18nLibFileSearch(string const & dir, string const & name,
+i18nLibFileSearch(string const & dir, string const & name,
string const & ext)
{
// this comment is from intl/dcigettext.c. We try to mimick this
- // behaviour here.
+ // behaviour here.
/* The highest priority value is the `LANGUAGE' environment
variable. But we don't use the value if the currently
selected locale is the C locale. This is a GNU extension. */
lang = GetEnv("LANG");
}
}
-
+
lang = token(lang, '_', 0);
-
+
if (lang.empty() || lang == "C")
return LibFileSearch(dir, name, ext);
else {
}
+string const LibScriptSearch(string const & command)
+{
+ string script;
+ string args = command;
+ split(args, script, ' ');
+ script = LibFileSearch("scripts", script);
+ if (script.empty())
+ return command;
+ else if (args.empty())
+ return script;
+ else
+ return script + ' ' + args;
+}
+
+
string const GetEnv(string const & envname)
{
// f.ex. what about error checking?
#if HAVE_PUTENV
// this leaks, but what can we do about it?
- // Is doing a getenv() and a free() of the older value
+ // Is doing a getenv() and a free() of the older value
// a good idea? (JMarc)
// Actually we don't have to leak...calling putenv like this
// should be enough: ... and this is obviously not enough if putenv
// I will enable the above.
//int retval = lyx::putenv(envstr.c_str());
#else
-#ifdef HAVE_SETENV
+#ifdef HAVE_SETENV
string varname;
string const str = envstr.split(varname,'=');
int const retval = ::setenv(varname.c_str(), str.c_str(), true);
int return_value = 0;
while ((de = readdir(dir))) {
string const temp = de->d_name;
- if (temp == "." || temp == "..")
+ if (temp == "." || temp == "..")
continue;
string const unlinkpath = AddName (path, temp);
- lyxerr[Debug::FILES] << "Deleting file: " << unlinkpath
+ lyxerr[Debug::FILES] << "Deleting file: " << unlinkpath
<< endl;
bool deleted = true;
if (fi.isOK() && fi.isDir())
deleted = (DeleteAllFilesInDir(unlinkpath) == 0);
deleted &= (lyx::unlink(unlinkpath) == 0);
- if (!deleted) {
- Alert::err_alert(_("Error! Could not remove file:"),
+ if (!deleted) {
+ Alert::err_alert(_("Error! Could not remove file:"),
unlinkpath);
return_value = -1;
}
- }
+ }
closedir(dir);
return return_value;
}
lyxerr[Debug::FILES]
<< "CreateTmpDir: tempdir=`" << tempdir << "'\n"
<< "CreateTmpDir: mask=`" << mask << "'" << endl;
-
+
string const tmpfl(lyx::tempName(tempdir, mask));
// lyx::tempName actually creates a file to make sure that it
// stays unique. So we have to delete it before we can create
// a dir with the same name. Note also that we are not thread
// safe because of the gap between unlink and mkdir. (Lgb)
lyx::unlink(tmpfl.c_str());
-
+
if (tmpfl.empty() || lyx::mkdir(tmpfl, 0700)) {
Alert::err_alert(_("Error! Couldn't create temporary directory:"),
tempdir);
if (Allfiles && DeleteAllFilesInDir(tmpdir)) {
return -1;
}
- if (lyx::rmdir(tmpdir)) {
- Alert::err_alert(_("Error! Couldn't delete temporary directory:"),
+ if (lyx::rmdir(tmpdir)) {
+ Alert::err_alert(_("Error! Couldn't delete temporary directory:"),
tmpdir);
return -1;
}
- return 0;
+ return 0;
}
} // namespace anon
}
+// FIXME: no need for separate method like this ...
int DestroyLyXTmpDir(string const & tmpdir)
{
- return DestroyTmpDir (tmpdir, false); // Why false?
+ return DestroyTmpDir(tmpdir, true);
}
TempBase = BasePath;
else
TempBase = AddPath(lyx::getcwd(), BasePath);
-
+
// Handle /./ at the end of the path
while (suffixIs(TempBase, "/./"))
TempBase.erase(TempBase.length() - 2);
while (!RTemp.empty()) {
// Split by next /
RTemp = split(RTemp, Temp, '/');
-
+
if (Temp == ".") continue;
if (Temp == "..") {
// Remove one level of TempBase
while (!RTemp.empty()) {
// Split by next /
RTemp = split(RTemp, Temp, '/');
-
+
if (Temp == ".") {
TempBase = "./";
} else if (Temp == "..") {
}
// returns absolute path
- return TempBase;
+ return TempBase;
}
//
// Search ${...} as Variable-Name inside the string and replace it with
// the denoted environmentvariable
-// Allow Variables according to
+// Allow Variables according to
// variable := '$' '{' [A-Za-z_]{[A-Za-z_0-9]*} '}'
//
string const ReplaceEnvironmentPath(string const & path)
{
-//
+//
// CompareChar: Environmentvariables starts with this character
// PathChar: Next path component start with this character
// while CompareChar found do:
// Split String with PathChar
-// Search Environmentvariable
+// Search Environmentvariable
// if found: Replace Strings
//
char const CompareChar = '$';
- char const FirstChar = '{';
- char const EndChar = '}';
- char const UnderscoreChar = '_';
+ char const FirstChar = '{';
+ char const EndChar = '}';
+ char const UnderscoreChar = '_';
string EndString; EndString += EndChar;
string FirstString; FirstString += FirstChar;
string CompareString; CompareString += CompareChar;
string result0 = split(path, result1, CompareChar);
while (!result0.empty()) {
string copy1(result0); // contains String after $
-
+
// Check, if there is an EndChar inside original String.
-
+
if (!regexMatch(copy1, RegExp)) {
// No EndChar inside. So we are finished
result1 += CompareString + result0;
bool result = isalpha(*cp1) || (*cp1 == UnderscoreChar);
++cp1;
while (*cp1 && result) {
- result = isalnum(*cp1) ||
- (*cp1 == UnderscoreChar);
+ result = isalnum(*cp1) ||
+ (*cp1 == UnderscoreChar);
++cp1;
}
result1 += res1;
continue;
}
-
+
string env(GetEnv(res1_contents + 1));
if (!env.empty()) {
// Congratulations. Environmentvariable found
// Next $-Sign?
result0 = split(res0, res1, CompareChar);
result1 += res1;
- }
+ }
return result1;
} // ReplaceEnvironmentPath
}
if (!path2.empty()) {
- string::size_type const p2start = path2.find_first_not_of('/');
+ string::size_type const p2start = path2.find_first_not_of('/');
string::size_type const p2end = path2.find_last_not_of('/');
string const tmp = path2.substr(p2start, p2end - p2start + 1);
buf += tmp + '/';
}
-/*
+/*
Change extension of oldname to extension.
Strips path off if no_path == true.
If no extension on oldname, just appends.
string::size_type last_dot = oldname.rfind('.');
if (last_dot < last_slash && last_slash != string::npos)
last_dot = string::npos;
-
+
string ext;
// Make sure the extension starts with a dot
if (!extension.empty() && extension[0] != '.')
}
// the different filetypes and what they contain in one of the first lines
-// (dots are any characters). (Herbert 20020131)
+// (dots are any characters). (Herbert 20020131)
+// AGR Grace...
+// BMP BM...
// EPS %!PS-Adobe-3.0 EPSF...
+// EPSI like EPS and with
+// %%BeginPreview...
+// FITS ...BITPIX...
// GIF GIF...
// JPG JFIF
// PDF %PDF-...
// PNG .PNG...
-// PS %!PS-Adobe-2.0
-// XBM static char ...
-// XPM /* XPM */
-/// return the "extension" which belongs to the contents
-string const getExtFromContents(string const & filename) {
- if (filename.empty() || !IsFileReadable(filename))
- return string(); // paranoia check
+// PBM P1... or P4 (B/W)
+// PGM P2... or P5 (Grayscale)
+// PPM P3... or P6 (color)
+// PS %!PS-Adobe-2.0 or 1.0, no "EPSF"!
+// SGI \001\332... (decimal 474)
+// TGIF %TGIF...
+// TIFF II... or MM...
+// XBM ..._bits[]...
+// XPM /* XPM */ sometimes missing (f.ex. tgif-export)
+// ...static char *...
+// XWD \000\000\000\151 (0x00006900) decimal 105
+//
+// GZIP \037\213\010\010... http://www.ietf.org/rfc/rfc1952.txt
+// ZIP PK... http://www.halyava.ru/document/ind_arch.htm
+// Z \037\177 UNIX compress
+
+/// return the "extension" which belongs to the contents.
+/// for no knowing contents return the extension. Without
+/// an extension and unknown contents we return "user"
+string const getExtFromContents(string const & filename)
+{
+ // paranoia check
+ if (filename.empty() || !IsFileReadable(filename))
+ return string();
+
+
ifstream ifs(filename.c_str());
- if (!ifs)
- return string(); // Couldn't open file...
- int const max_count = 50; // Maximum strings to read to attempt recognition
- int count = 0; // Counter of attempts.
- string str;
- for (; count < max_count; ++count) {
+ if (!ifs)
+ // Couldn't open file...
+ return string();
+
+ // gnuzip
+ string const gzipStamp = "\037\213\010\010";
+
+ // PKZIP
+ string const zipStamp = "PK";
+
+ // compress
+ string const compressStamp = "\037\177";
+
+ // Maximum strings to read
+ int const max_count = 50;
+ int count = 0;
+
+ string str, format;
+ bool firstLine = true;
+ while ((count++ < max_count) && format.empty()) {
if (ifs.eof()) {
- lyxerr[Debug::INFO] << "InsetGraphics (classifyFiletype)"
- " End of file reached and it wasn't found to be a known Type!" << endl;
+ lyxerr[Debug::GRAPHICS]
+ << "filetools(getExtFromContents)\n"
+ << "\tFile type not recognised before EOF!"
+ << endl;
break;
}
- ifs >> str;
- if (contains(str,"EPSF"))
- return "eps";
- else if (contains(str,"GIF"))
- return "gif";
+
+ getline(ifs, str);
+
+ lyxerr[Debug::GRAPHICS] << "Scanstring: " << str.substr(0,60)
+ << endl;
+
+ string const stamp = str.substr(0,2);
+ if (firstLine && str.size() >= 2) {
+ // at first we check for a zipped file, because this
+ // information is saved in the first bytes of the file!
+ // also some graphic formats which save the information
+ // in the first line, too.
+ if (prefixIs(str, gzipStamp)) {
+ format = "gzip";
+
+ } else if (stamp == zipStamp) {
+ format = "zip";
+
+ } else if (stamp == compressStamp) {
+ format = "compress";
+
+ // the graphics part
+ } else if (stamp == "BM") {
+ format = "bmp";
+
+ } else if (stamp == "\001\332") {
+ format = "sgi";
+
+ // PBM family
+ // Don't need to use str.at(0), str.at(1) because
+ // we already know that str.size() >= 2
+ } else if (str[0] == 'P') {
+ switch (str[1]) {
+ case '1':
+ case '4':
+ format = "pbm";
+ break;
+ case '2':
+ case '5':
+ format = "pgm";
+ break;
+ case '3':
+ case '6':
+ format = "ppm";
+ }
+ break;
+
+ } else if ((stamp == "II") || (stamp == "MM")) {
+ format = "tiff";
+
+ } else if (prefixIs(str,"%TGIF")) {
+ format = "tgif";
+
+ } else if (prefixIs(str,"GIF")) {
+ format = "gif";
+
+ } else if (str.size() > 3) {
+ int const c = ((str[0] << 24) & (str[1] << 16) &
+ (str[2] << 8) & str[3]);
+ if (c == 105) {
+ format = "xwd";
+ }
+ }
+
+ firstLine = false;
+ }
+
+ if (!format.empty())
+ break;
+ else if (contains(str,"EPSF"))
+ // dummy, if we have wrong file description like
+ // %!PS-Adobe-2.0EPSF"
+ format = "eps";
+
+ else if (contains(str,"Grace"))
+ format = "agr";
+
else if (contains(str,"JFIF"))
- return "jpg";
+ format = "jpg";
+
else if (contains(str,"%PDF"))
- return "pdf";
+ format = "pdf";
+
else if (contains(str,"PNG"))
- return "png";
- else if (contains(str,"%!PS-Adobe-"))
- return "ps"; // eps here no more possible
- else if (contains(str,"static char"))
- return "xbm";
- else if (contains(str,"XPM"))
- return "xpm";
+ format = "png";
+
+ else if (contains(str,"%!PS-Adobe")) {
+ // eps or ps
+ ifs >> str;
+ if (contains(str,"EPSF"))
+ format = "eps";
+ else
+ format = "ps";
+ }
+
+ else if (contains(str,"_bits[]"))
+ format = "xbm";
+
+ else if (contains(str,"XPM") || contains(str, "static char *"))
+ format = "xpm";
+
+ else if (contains(str,"BITPIX"))
+ format = "fits";
}
- lyxerr[Debug::INFO] << "InsetGraphics (classifyFiletype)"
- " Couldn't find a known Type!" << endl;
- return string();
+
+ if (!format.empty()) {
+ // if we have eps than epsi is also possible
+ // we have to check for a preview
+ if (format == "eps") {
+ lyxerr[Debug::GRAPHICS]
+ << "\teps detected -> test for an epsi ..."
+ << endl;
+ while (count++ < max_count) {
+ if (ifs.eof())
+ break;
+ getline(ifs, str);
+ if (contains(str, "BeginPreview")) {
+ format = "epsi";
+ count = max_count;
+ }
+ }
+ }
+ lyxerr[Debug::GRAPHICS]
+ << "Recognised Fileformat: " << format << endl;
+ return format;
+ }
+
+ string const ext(GetExtension(filename));
+ lyxerr[Debug::GRAPHICS]
+ << "filetools(getExtFromContents)\n"
+ << "\tCouldn't find a known Type!\n";
+ if (!ext.empty()) {
+ lyxerr[Debug::GRAPHICS]
+ << "\twill take the file extension -> "
+ << ext << endl;
+ return ext;
+ } else {
+ lyxerr[Debug::GRAPHICS]
+ << "\twill use ext or a \"user\" defined format" << endl;
+ return "user";
+ }
+}
+
+
+/// check for zipped file
+bool zippedFile(string const & name)
+{
+ string const type = getExtFromContents(name);
+ if (contains("gzip zip compress", type) && !type.empty())
+ return true;
+ return false;
+}
+
+
+string const unzipFile(string const & zipped_file)
+{
+ string const file = ChangeExtension(zipped_file, string());
+ string const tempfile = lyx::tempName(string(), file);
+ // Run gunzip
+ string const command = "gunzip -c " + zipped_file + " > " + tempfile;
+ Systemcall one;
+ one.startscript(Systemcall::Wait, command);
+ // test that command was executed successfully (anon)
+ // yes, please do. (Lgb)
+ return tempfile;
}
if (l2 > threshold) {
// Yes, shortend it
prefix += ".../";
-
+
string temp;
-
+
while (relhome.length() > threshold)
relhome = split(relhome, temp, '/');
// of course the best would be to have a
// pstream (process stream), with the
// variants ipstream, opstream
- FILE * inf = ::popen(cmd.c_str(), "r");
+
+ FILE * inf = ::popen(cmd.c_str(), os::read_mode());
+
+ // (Claus Hentschel) Check if popen was succesful ;-)
+ if (!inf)
+ return make_pair(-1, string());
+
string ret;
int c = fgetc(inf);
while (c != EOF) {
going to implement this until I see some demand for it.
Lgb
*/
-
+
// If the file can be found directly, we just return a
- // absolute path version of it.
- if (FileInfo(fil).exist())
+ // absolute path version of it.
+ if (FileInfo(fil).exist())
return MakeAbsPath(fil);
- // No we try to find it using kpsewhich.
+ // No we try to find it using kpsewhich.
// It seems from the kpsewhich manual page that it is safe to use
// kpsewhich without --format: "When the --format option is not
// given, the search path used when looking for a file is inferred
cmdret const c = do_popen(kpsecmd);
lyxerr[Debug::LATEX] << "kpse status = " << c.first << "\n"
- << "kpse result = `" << strip(c.second, '\n')
+ << "kpse result = `" << strip(c.second, '\n')
<< "'" << endl;
- if (c.first != -1)
+ if (c.first != -1)
return os::internal_path(strip(strip(c.second, '\n'), '\r'));
else
return string();
}
}
+
+void readBB_lyxerrMessage(string const & file, bool & zipped,
+ string const & message)
+{
+ lyxerr[Debug::GRAPHICS] << "[readBB_from_PSFile] "
+ << message << std::endl;
+ if (zipped)
+ lyx::unlink(file);
+}
+
+
+string const readBB_from_PSFile(string const & file)
+{
+ // in a (e)ps-file it's an entry like %%BoundingBox:23 45 321 345
+ // It seems that every command in the header has an own line,
+ // getline() should work for all files.
+ // On the other hand some plot programs write the bb at the
+ // end of the file. Than we have in the header:
+ // %%BoundingBox: (atend)
+ // In this case we must check the end.
+ bool zipped = zippedFile(file);
+ string const file_ = zipped ?
+ string(unzipFile(file)) : string(file);
+ string const format = getExtFromContents(file_);
+
+ if (format != "eps" && format != "ps") {
+ readBB_lyxerrMessage(file_, zipped,"no(e)ps-format");
+ return string();
+ }
+
+ std::ifstream is(file_.c_str());
+ while (is) {
+ string s;
+ getline(is,s);
+ if (contains(s,"%%BoundingBox:") && !contains(s,"atend")) {
+ string const bb = frontStrip(s.substr(14));
+ readBB_lyxerrMessage(file_, zipped, bb);
+ return bb;
+ }
+ }
+ readBB_lyxerrMessage(file_, zipped, "no bb found");
+ return string();
+}