ngspice/src/misc/tilde.c

226 lines
6.6 KiB
C

/**********
Copyright 1991 Regents of the University of California. All rights reserved.
Modified: 2002 R. Oktas, <roktas@omu.edu.tr>
**********/
#include "ngspice/defines.h"
#include "ngspice/ngspice.h"
#include "ngspice/stringskip.h"
#include "tilde.h"
#ifdef HAVE_PWD_H
#include <pwd.h>
#endif
#ifdef _WIN32
#undef BOOLEAN
#include <windows.h> /* win32 functions */
#include "shlobj.h" /* SHGetFolderPath */
#endif
static inline int copy_home_to_buf(size_t n_byte_dst, char **p_dst,
const char *src);
/* XXX To prevent a name collision with `readline's `tilde_expand',
the original name: `tilde_expand' has changed to `tildexpand'. This
situation naturally brings to mind that `tilde_expand' could be used
directly from `readline' (since it will already be included if we
wish to activate the `readline' support). Following implementation of
'tilde expanding' has some problems which constitutes another good
reason why it should be replaced: eg. it returns NULL which should
not behave this way, IMHO. Anyway... Don't care for the moment, may
be in the future. -- ro */
char *tildexpand(const char *string)
{
/* If no string passed, return NULL */
if (!string) {
return NULL;
}
string = skip_ws(string); /* step past leading whitespace */
/* If the string does not begin with a tilde, there is no ~ to expand */
if (*string != '~') {
return copy(string);
}
++string; /* step past tilde */
/* Test for home of current user */
if (*string == '\0' || *string == DIR_TERM) {
char *sz_home;
const int n_char_home = get_local_home(0, &sz_home);
if (n_char_home < 0) {
return copy(string); /* Strip the ~ and return the rest */
}
const size_t n_char_rest = strlen(string);
TREALLOC(char, sz_home, (size_t) n_char_home + n_char_rest + 1);
strcpy(sz_home + n_char_home, string);
return sz_home;
}
#ifdef HAVE_PWD_H
/* ~bob -- Get name of user and find home for that user */
{
char buf_fixed[100];
char *buf = buf_fixed;
const char * const usr_start = string;
char c;
while ((c = *string) && c != '/') {
string++;
}
const char * const usr_end = string;
const size_t n_char_usr = usr_end - usr_start;
const size_t n_byte_usr = n_char_usr + 1;
if (n_byte_usr > sizeof buf_fixed) {
buf = TMALLOC(char, n_byte_usr);
}
(void) memcpy(buf, usr_start, n_char_usr);
buf[n_char_usr] = '\0';
char *sz_home;
const int n_char_home = get_usr_home(buf, 0, &sz_home);
if (buf != buf_fixed) { /* free allocated buffer for user name */
txfree(buf);
}
if (n_char_home < 0) {
return copy(usr_start); /* Strip the ~ and return the rest */
}
const size_t n_char_rest = strlen(string);
TREALLOC(char, sz_home, (size_t) n_char_home + n_char_rest + 1);
strcpy(sz_home + n_char_home, string);
return sz_home;
}
#else
/* ~abc is meaningless */
return copy(string); /* Again strip ~ and return rest */
#endif
} /* end of function tildexpand */
/* Get value of "HOME" for the current user and copy to *p_buf if
* the value is less than n_byte_buf characters long. Otherwise
* allocate a buffer of the minimum required size.
*
* Return values
* >0: Number of characters copied to *p_buf, excluding the trailing null
* -1: A value for HOME could not be obtained.
*
* Remarks:
* This function does not free any allocation at *p_buf, allowing a
* fixed buffer to be passed to it.
*/
int get_local_home(size_t n_byte_buf, char **p_buf)
{
char *sz_home = (char *) NULL;
#ifdef _WIN32
char buf_sh_path[MAX_PATH];
#endif
do {
/* First thing to try is an environment variable HOME */
if ((sz_home = getenv("HOME")) != (char *) NULL) {
break;
}
#if defined(_WIN32)
/* If Windows, try an env var USERPROFILE next */
if ((sz_home = getenv("USERPROFILE")) != (char *) NULL) {
break;
}
/* For Windows, the folder path CSIDL_PERSONAL is tried next */
if (SUCCEEDED(SHGetFolderPath(NULL, CSIDL_PERSONAL, NULL, 0,
buf_sh_path))) {
sz_home = buf_sh_path;
break;
}
#elif defined(HAVE_PWD_H) /* _WIN32 and HAVE_PWD_H are mutually exclusive */
/* Get home information for the current user */
{
struct passwd *pw;
pw = getpwuid(getuid());
if (pw) {
sz_home = pw->pw_dir;
}
}
#endif
} while (0);
if (sz_home == (char *) NULL) { /* did not find a HOME value */
return -1;
}
/* Copy home value to buffer */
return copy_home_to_buf(n_byte_buf, p_buf, sz_home);
} /* end of function get_local_home */
#ifdef HAVE_PWD_H
/* Get value of "HOME" for usr and copy to *p_buf if
* the value is less than n_byte_buf characters long. Otherwise
* allocate a buffer of the minimum required size.
*
* Return values
* >0: Number of characters copied to *pp_buf, excluding the trailing null
* -1: A value for HOME could not be obtained.
*
* Remarks:
* This function does not free any allocation at *p_buf, allowing a
* fixed buffer to be passed to it.
*/
int get_usr_home(const char *usr, size_t n_byte_buf, char **p_buf)
{
struct passwd * const pw = getpwnam(usr);
if (pw) {
/* Copy home value to buffer */
return copy_home_to_buf(n_byte_buf, p_buf, pw->pw_dir);
}
return -1;
} /* end of function get_usr_home */
#endif /* HAVE_PWD_H */
/* This function copies home value src to the buffer at *p_dst, allocating
* a larger buffer if required.
*
* Parameters
* n_byte_dst: Size of supplied destination buffer
* p_dst: Address containing address of supplied buffer on input. May be
* given the address of a larger buffer allocation if the input buffer
* is too small.
* src: Address of HOME value
*
* Return values
* number of characters copied excluding terminating null
*
* Remarks:
* This function does not free any allocation at *p_dst, allowing a
* fixed buffer to be passed to it.
*/
static inline int copy_home_to_buf(size_t n_byte_dst, char **p_dst,
const char *src)
{
const size_t n_char_src = strlen(src); /* Size of HOME value */
const size_t n_byte_src = n_char_src + 1;
/* Allocate dst if input buffer too small */
if (n_byte_src > n_byte_dst) { /* too big */
*p_dst = TMALLOC(char, n_byte_src);
}
(void) memcpy(*p_dst, src, n_byte_src);
return (int) n_char_src;
} /* end of function copy_home_to_buf */