blob: a4dd5ceca1a444da3b98399b7c7f74699ffbab41 [file] [log] [blame]
/* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
/*
* OS abstraction functions. Small functions should be defined
* as "__inline" in os.h.
*/
#include <sys/stat.h>
#include <stdio.h>
#include <time.h>
#include "os.h"
#include "errno.h"
/* Win95 doesn't like trailing /s. NT and Unix don't mind. This works
* around the problem.
* Errr... except if it is UNC and we are referring to the root of
* the UNC, we MUST have a trailing \ and we can't use /s. Jeez.
* Not sure if this refers to all UNCs or just roots,
* but I'm going to fix it for all cases for now. (Ben)
*/
#undef stat
API_EXPORT(int) os_stat(const char *szPath, struct stat *pStat)
{
int n;
int len = strlen(szPath);
if (len == 0) {
errno = ENOENT;
return -1;
}
if (len >= MAX_PATH) {
errno = ENAMETOOLONG;
return -1;
}
if (szPath[0] == '/' && szPath[1] == '/') {
char buf[MAX_PATH];
char *s;
int nSlashes = 0;
strcpy(buf, szPath);
for (s = buf; *s; ++s) {
if (*s == '/') {
*s = '\\';
++nSlashes;
}
}
/* then we need to add one more to get \\machine\share\ */
if (nSlashes == 3) {
if (++len >= MAX_PATH) {
errno = ENAMETOOLONG;
return -1;
}
*s++ = '\\';
}
*s = '\0';
return stat(buf, pStat);
}
/*
* Below removes the trailing /, however, do not remove
* it in the case of 'x:/' or stat will fail
*/
n = strlen(szPath);
if ((szPath[n - 1] == '\\' || szPath[n - 1] == '/') &&
!(n == 3 && szPath[1] == ':')) {
char buf[MAX_PATH];
strcpy(buf, szPath);
buf[n - 1] = '\0';
return stat(buf, pStat);
}
return stat(szPath, pStat);
}
/* Fix two really crap problems with Win32 spawn[lv]e*:
*
* 1. Win32 doesn't deal with spaces in argv.
* 2. Win95 doesn't like / in cmdname.
*/
#undef _spawnv
API_EXPORT(int) os_spawnv(int mode, const char *cmdname,
const char *const *argv)
{
int n;
char **aszArgs;
const char *szArg;
char *szCmd;
char *s;
szCmd = _alloca(strlen(cmdname)+1);
strcpy(szCmd, cmdname);
for (s = szCmd; *s; ++s) {
if (*s == '/') {
*s = '\\';
}
}
for (n = 0; argv[n]; ++n)
;
aszArgs = _alloca((n + 1) * sizeof(const char *));
for (n = 0; szArg = argv[n]; ++n) {
if (strchr(szArg, ' ')) {
int l = strlen(szArg);
aszArgs[n] = _alloca(l + 2 + 1);
aszArgs[n][0] = '"';
strcpy(&aszArgs[n][1], szArg);
aszArgs[n][l + 1] = '"';
aszArgs[n][l + 2] = '\0';
}
else {
aszArgs[n] = (char *)szArg;
}
}
aszArgs[n] = NULL;
return _spawnv(mode, szCmd, aszArgs);
}
#undef _spawnve
API_EXPORT(int) os_spawnve(int mode, const char *cmdname,
const char *const *argv, const char *const *envp)
{
int n;
char **aszArgs;
const char *szArg;
char *szCmd;
char *s;
szCmd = _alloca(strlen(cmdname)+1);
strcpy(szCmd, cmdname);
for (s = szCmd; *s; ++s) {
if (*s == '/') {
*s = '\\';
}
}
for (n = 0; argv[n]; ++n)
;
aszArgs = _alloca((n + 1)*sizeof(const char *));
for (n = 0; szArg = argv[n]; ++n){
if (strchr(szArg, ' ')) {
int l = strlen(szArg);
aszArgs[n] = _alloca(l + 2 + 1);
aszArgs[n][0] = '"';
strcpy(&aszArgs[n][1], szArg);
aszArgs[n][l + 1] = '"';
aszArgs[n][l + 2] = '\0';
}
else {
aszArgs[n] = (char *)szArg;
}
}
aszArgs[n] = NULL;
return _spawnve(mode, szCmd, aszArgs, envp);
}
API_EXPORT_NONSTD(int) os_spawnle(int mode, const char *cmdname, ...)
{
int n;
va_list vlist;
char **aszArgs;
const char *szArg;
const char *const *aszEnv;
char *szCmd;
char *s;
szCmd = _alloca(strlen(cmdname)+1);
strcpy(szCmd, cmdname);
for (s = szCmd; *s; ++s) {
if (*s == '/') {
*s = '\\';
}
}
va_start(vlist, cmdname);
for (n = 0; va_arg(vlist, const char *); ++n)
;
va_end(vlist);
aszArgs = _alloca((n + 1) * sizeof(const char *));
va_start(vlist, cmdname);
for (n = 0; szArg = va_arg(vlist, const char *); ++n) {
if (strchr(szArg, ' ')) {
int l = strlen(szArg);
aszArgs[n] = _alloca(l + 2 + 1);
aszArgs[n][0] = '"';
strcpy(&aszArgs[n][1], szArg);
aszArgs[n][l + 1] = '"';
aszArgs[n][l + 2] = '\0';
}
else {
aszArgs[n] = (char *)szArg;
}
}
aszArgs[n] = NULL;
aszEnv = va_arg(vlist, const char *const *);
va_end(vlist);
return _spawnve(mode, szCmd, aszArgs, aszEnv);
}
#undef strftime
/* Partial replacement for strftime. This adds certain expandos to the
* Windows version
*/
API_EXPORT(int) os_strftime(char *s, size_t max, const char *format,
const struct tm *tm) {
/* If the new format string is bigger than max, the result string probably
* won't fit anyway. When %-expandos are added, made sure the padding below
* is enough.
*/
char *new_format = (char *) _alloca(max + 11);
size_t i, j, format_length = strlen(format);
int return_value;
int length_written;
for (i = 0, j = 0; (i < format_length && j < max);) {
if (format[i] != '%') {
new_format[j++] = format[i++];
continue;
}
switch (format[i+1]) {
case 'D':
/* Is this locale dependent? Shouldn't be...
Also note the year 2000 exposure here */
memcpy(new_format + j, "%m/%d/%y", 8);
i += 2;
j += 8;
break;
case 'r':
memcpy(new_format + j, "%I:%M:%S %p", 11);
i += 2;
j += 11;
break;
case 'T':
memcpy(new_format + j, "%H:%M:%S", 8);
i += 2;
j += 8;
break;
case 'e':
length_written = _snprintf(new_format + j, max - j, "%2d",
tm->tm_mday);
j = (length_written == -1) ? max : (j + length_written);
i += 2;
break;
default:
/* We know we can advance two characters forward here. */
new_format[j++] = format[i++];
new_format[j++] = format[i++];
}
}
if (j >= max) {
*s = '\0'; /* Defensive programming, okay since output is undefined */
return_value = 0;
} else {
new_format[j] = '\0';
return_value = strftime(s, max, new_format, tm);
}
return return_value;
}