/* PSPP - a program for statistical analysis.
Copyright (C) 1997-9, 2000, 2006, 2007, 2009, 2010, 2011 Free Software Foundation, Inc.
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 3 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, see . */
#include
#include "data/file-name.h"
#include "data/file-handle-def.h"
#include
#include
#include
#include
#include
#include
#include
#include "data/settings.h"
#include "libpspp/hash-functions.h"
#include "libpspp/message.h"
#include "libpspp/i18n.h"
#include "libpspp/str.h"
#include "libpspp/version.h"
#include "gl/dirname.h"
#include "gl/intprops.h"
#include "gl/minmax.h"
#include "gl/relocatable.h"
#include "gl/xalloc.h"
#include "gl/xmalloca.h"
#include "gettext.h"
#include "xvasprintf.h"
#define _(msgid) gettext (msgid)
/* Functions for performing operations on file names. */
/* Returns the extension part of FILE_NAME as a malloc()'d string.
If FILE_NAME does not have an extension, returns an empty
string. */
char *
fn_extension (const struct file_handle *fh)
{
const char *file_name = fh_get_file_name (fh);
const char *extension = strrchr (file_name, '.');
if (extension == NULL)
extension = "";
return xstrdup (extension);
}
/* Find out information about files. */
/* Returns true iff NAME specifies an absolute file name. */
static bool
fn_is_absolute (const char *name)
{
return IS_ABSOLUTE_FILE_NAME (name);
}
/* Searches for a file with name NAME in the directories given in
PATH, which is terminated by a null pointer. Returns the full name of the
first file found, which the caller is responsible for freeing with free(),
or NULL if none is found. */
char *
fn_search_path (const char *base_name, char **path)
{
size_t i;
if (fn_is_absolute (base_name))
return xstrdup (base_name);
for (i = 0; path[i] != NULL; i++)
{
const char *dir = path[i];
char *file;
if (!strcmp (dir, "") || !strcmp (dir, "."))
file = xstrdup (base_name);
else if (ISSLASH (dir[strlen (dir) - 1]))
file = xasprintf ("%s%s", dir, base_name);
else
file = xasprintf ("%s/%s", dir, base_name);
struct stat temp;
if (((stat (file, &temp) == 0) && (! S_ISDIR (temp.st_mode))))
return file;
free (file);
}
return NULL;
}
/* Returns true if file with name NAME exists, and that file is not a
directory */
bool
fn_exists (const struct file_handle *fh)
{
const char *name = fh_get_file_name (fh);
struct stat temp;
if (stat (name, &temp) != 0)
return false;
return ! S_ISDIR (temp.st_mode);
}
/* Basic file handling. */
#if HAVE_POPEN
/* Used for giving an error message on a set_safer security
violation. */
static FILE *
safety_violation (const char *fn)
{
msg (SE, _("Not opening pipe file `%s' because %s option set."), fn, "SAFER");
errno = EPERM;
return NULL;
}
#endif
/* File open routine that understands `-' as stdin/stdout and `|cmd'
as a pipe to command `cmd'. Returns resultant FILE on success,
NULL on failure. If NULL is returned then errno is set to a
sensible value. */
FILE *
fn_open (const struct file_handle *fh, const char *mode)
{
const char *fn = fh_get_file_name (fh);
assert (mode[0] == 'r' || mode[0] == 'w' || mode[0] == 'a');
if (mode[0] == 'r')
{
if (!strcmp (fn, "stdin") || !strcmp (fn, "-"))
return stdin;
}
else
{
if (!strcmp (fn, "stdout") || !strcmp (fn, "-"))
return stdout;
if (!strcmp (fn, "stderr"))
return stderr;
}
#if HAVE_POPEN
if (fn[0] == '|')
{
if (settings_get_safer_mode ())
return safety_violation (fn);
return popen (&fn[1], mode[0] == 'r' ? "r" : "w");
}
else if (*fn && fn[strlen (fn) - 1] == '|')
{
char *s;
FILE *f;
if (settings_get_safer_mode ())
return safety_violation (fn);
s = xmalloca (strlen (fn));
memcpy (s, fn, strlen (fn) - 1);
s[strlen (fn) - 1] = 0;
f = popen (s, mode[0] == 'r' ? "r" : "w");
freea (s);
return f;
}
else
#endif
#if WIN32
{
wchar_t *ss = convert_to_filename_encoding (fn, strlen (fn), fh_get_file_name_encoding (fh));
wchar_t *m = (wchar_t *) recode_string ("UTF-16LE", "ASCII", mode, strlen (mode));
FILE *fp = _wfopen (ss, m);
free (m);
free (ss);
return fp;
}
#else
return fopen (fn, mode);
#endif
}
/* Counterpart to fn_open that closes file F with name FN; returns 0
on success, EOF on failure. If EOF is returned, errno is set to a
sensible value. */
int
fn_close (const struct file_handle *fh, FILE *f)
{
const char *fn = fh_get_file_name (fh);
if (fileno (f) == STDIN_FILENO
|| fileno (f) == STDOUT_FILENO
|| fileno (f) == STDERR_FILENO)
return 0;
#if HAVE_POPEN
else if (fn[0] == '|' || (*fn && fn[strlen (fn) - 1] == '|'))
{
pclose (f);
return 0;
}
#endif
else
return fclose (f);
}
#ifdef WIN32
/* Apparently windoze users like to see output dumped into their home directory,
not the current directory (!) */
const char *
default_output_path (void)
{
static char *path = NULL;
if (path == NULL)
{
/* Windows NT defines HOMEDRIVE and HOMEPATH. But give preference
to HOME, because the user can change HOME. */
const char *home_dir = getenv ("HOME");
int i;
if (home_dir == NULL)
{
const char *home_drive = getenv ("HOMEDRIVE");
const char *home_path = getenv ("HOMEPATH");
if (home_drive != NULL && home_path != NULL)
home_dir = xasprintf ("%s%s",
home_drive, home_path);
}
if (home_dir == NULL)
home_dir = "c:/users/default"; /* poor default */
/* Copy home_dir into path. Add a slash at the end but
only if there isn't already one there, because Windows
treats // specially. */
if (home_dir[0] == '0円'
|| strchr ("/\\", home_dir[strlen (home_dir) - 1]) == NULL)
path = xasprintf ("%s%c", home_dir, '/');
else
path = xstrdup (home_dir);
for(i = 0; i < strlen (path); i++)
if (path[i] == '\\') path[i] = '/';
}
return path;
}
const char *
default_log_path (void)
{
return default_output_path ();
}
#else
/* ... whereas the rest of the world just likes it to be
put "here" for easy access. */
const char *
default_output_path (void)
{
static char current_dir[] = "";
return current_dir;
}
const char *
default_log_path (void)
{
static char *log_path = NULL;
if (!log_path)
{
char *tmp = NULL;
const char *state_home = getenv ("XDG_STATE_HOME");
if (!state_home)
{
const char *home = getenv ("HOME");
state_home = tmp = xasprintf ("%s/.local/state", home ? home : "");
}
log_path = xasprintf ("%s/pspp/", state_home);
struct stat s;
if (!stat (state_home, &s) && stat (log_path, &s) && errno == ENOENT)
mkdir (log_path, 0700);
free (tmp);
}
return log_path;
}
#endif