366 lines
8.4 KiB
C
366 lines
8.4 KiB
C
/*
|
|
* miscellaneous glibc extending functions
|
|
*
|
|
* This file is part of ANT (Ant is Not a Telephone)
|
|
*
|
|
* Copyright 2002, 2003 Roland Stigge
|
|
*
|
|
* ANT 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 2 of the License, or
|
|
* (at your option) any later version.
|
|
*
|
|
* ANT 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 ANT; if not, write to the Free Software
|
|
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
|
*
|
|
*/
|
|
|
|
#include "config.h"
|
|
|
|
/* regular GNU system includes */
|
|
#include <stdio.h>
|
|
#include <unistd.h>
|
|
#ifdef HAVE_STDLIB_H
|
|
#include <stdlib.h>
|
|
#endif
|
|
#ifdef HAVE_SYS_TIME_H
|
|
#include <sys/time.h>
|
|
#endif
|
|
#include <string.h>
|
|
#include <time.h>
|
|
#include <pwd.h>
|
|
#include <sys/stat.h>
|
|
#include <errno.h>
|
|
|
|
/* own header files */
|
|
#include "globals.h"
|
|
#include "util.h"
|
|
|
|
/* declared in globals.h, defined here */
|
|
int debug = 0;
|
|
|
|
/* Subtract the `struct timeval' values X and Y,
|
|
* storing the result in RESULT.
|
|
* Return 1 if the difference is negative, otherwise 0.
|
|
*/
|
|
int timeval_subtract (struct timeval *result,
|
|
struct timeval *x,
|
|
struct timeval *y) {
|
|
/* Perform the carry for the later subtraction by updating y. */
|
|
if (x->tv_usec < y->tv_usec) {
|
|
int nsec = (y->tv_usec - x->tv_usec) / 1000000 + 1;
|
|
y->tv_usec -= 1000000 * nsec;
|
|
y->tv_sec += nsec;
|
|
}
|
|
if (x->tv_usec - y->tv_usec > 1000000) {
|
|
int nsec = (x->tv_usec - y->tv_usec) / 1000000;
|
|
y->tv_usec += 1000000 * nsec;
|
|
y->tv_sec -= nsec;
|
|
}
|
|
|
|
/* Compute the time remaining to wait.
|
|
tv_usec is certainly positive. */
|
|
result->tv_sec = x->tv_sec - y->tv_sec;
|
|
result->tv_usec = x->tv_usec - y->tv_usec;
|
|
|
|
/* Return 1 if result is negative. */
|
|
return x->tv_sec < y->tv_sec;
|
|
}
|
|
|
|
/* sleeps usecs microseconds (will be interrupted by signals) */
|
|
void ant_sleep(int usecs) {
|
|
struct timespec requested;
|
|
struct timespec remaining;
|
|
|
|
requested.tv_sec = 0;
|
|
requested.tv_nsec = usecs * 1000;
|
|
nanosleep(&requested, &remaining);
|
|
}
|
|
|
|
/*
|
|
* convert vanity number (case insensitive: 'b','B' -> '2') in-place
|
|
*/
|
|
char *un_vanity(char *s) {
|
|
char *temp = s;
|
|
|
|
while (*temp) {
|
|
switch (*temp) {
|
|
case 'a': case 'b': case 'c': case 'A': case 'B': case 'C':
|
|
*temp = '2';
|
|
break;
|
|
case 'd': case 'e': case 'f': case 'D': case 'E': case 'F':
|
|
*temp = '3';
|
|
break;
|
|
case 'g': case 'h': case 'i': case 'G': case 'H': case 'I':
|
|
*temp = '4';
|
|
break;
|
|
case 'j': case 'k': case 'l': case 'J': case 'K': case 'L':
|
|
*temp = '5';
|
|
break;
|
|
case 'm': case 'n': case 'o': case 'M': case 'N': case 'O':
|
|
*temp = '6';
|
|
break;
|
|
case 'p': case 'q': case 'r': case 's':
|
|
case 'P': case 'Q': case 'R': case 'S':
|
|
*temp = '7';
|
|
break;
|
|
case 't': case 'u': case 'v': case 'T': case 'U': case 'V':
|
|
*temp = '8';
|
|
break;
|
|
case 'w': case 'x': case 'y': case 'z':
|
|
case 'W': case 'X': case 'Y': case 'Z':
|
|
*temp = '9';
|
|
break;
|
|
}
|
|
temp++;
|
|
}
|
|
|
|
return s;
|
|
}
|
|
|
|
/*
|
|
* returns a string representing the time difference between the
|
|
* specified times
|
|
*
|
|
* NOTE: caller has to free() the returned string itself.
|
|
*/
|
|
char *timediff_str(time_t time1, time_t time0) {
|
|
int duration;
|
|
char *buf;
|
|
int hours;
|
|
int minutes;
|
|
int seconds;
|
|
|
|
buf = (char *)malloc(9);
|
|
|
|
duration = (int)difftime(time1, time0);
|
|
seconds = duration % 60;
|
|
minutes = (duration / 60) % 60;
|
|
hours = duration / (60 * 60);
|
|
if (snprintf(buf, 9, "%02d:%02d:%02d", hours, minutes, seconds) >= 9)
|
|
buf[8] = '\0';
|
|
|
|
return buf;
|
|
}
|
|
|
|
/*
|
|
* returns a string with the specified long int as content
|
|
*
|
|
* NOTE: caller has to free() the returned memory
|
|
*/
|
|
char *ltostr(long int i) {
|
|
char *s;
|
|
|
|
if (asprintf(&s, "%ld", i) < 0)
|
|
return NULL;
|
|
|
|
return s;
|
|
}
|
|
|
|
/*
|
|
* forks and executes given command (with arguments) via sh
|
|
*/
|
|
void execute(char *command) {
|
|
pid_t result = fork();
|
|
char *argv[] = {"sh", "-c", command, NULL};
|
|
|
|
if (result == -1) { /* error */
|
|
fprintf(stderr, "Fork error.\n");
|
|
} else if (result == 0) { /* we are the child */
|
|
if (execvp("sh", argv)) {
|
|
fprintf(stderr, "Exec error.\n");
|
|
exit(1);
|
|
}
|
|
}
|
|
}
|
|
|
|
/*
|
|
* return home dir, returned buffer must not be freed but will be overwritten
|
|
* by subsequent calls (or calls to getpwuid())
|
|
* returns NULL on error
|
|
*/
|
|
char *get_homedir(void) {
|
|
struct passwd *user;
|
|
|
|
if (!(user = getpwuid(getuid()))) {
|
|
return NULL;
|
|
}
|
|
|
|
if (user->pw_dir == NULL || !strcmp(user->pw_dir, "") ||
|
|
user->pw_dir[strlen(user->pw_dir) - 1] == '/') {
|
|
return NULL;
|
|
}
|
|
|
|
return user->pw_dir;
|
|
}
|
|
|
|
/*
|
|
* When the specified directory doesn't exist, create it (755)
|
|
* (used by options and history write functions)
|
|
* returns 0 on success, -1 otherwise
|
|
*/
|
|
int touch_dir(char *dirname) {
|
|
|
|
if (!mkdir(dirname, S_IRWXU | S_IRGRP | S_IXGRP | S_IROTH | S_IXOTH))
|
|
return 0;
|
|
|
|
if (errno == EEXIST) return 0;
|
|
|
|
return -1;
|
|
}
|
|
|
|
/*
|
|
* Creates dotdir (in homedir) if it doesn't exist
|
|
*
|
|
* returns 0 on success, -1 otherwise
|
|
*/
|
|
int touch_dotdir(void) {
|
|
char *homedir;
|
|
char *filename;
|
|
|
|
if (!(homedir = get_homedir())) {
|
|
fprintf(stderr, "Warning: Couldn't get home dir.\n");
|
|
return -1;
|
|
}
|
|
|
|
if (asprintf(&filename, "%s/." PACKAGE, homedir) < 0) {
|
|
fprintf(stderr,
|
|
"Warning: Couldn't allocate memory for configuration directory.\n");
|
|
return -1;
|
|
}
|
|
if (touch_dir(filename) < 0) {
|
|
if (debug)
|
|
fprintf(stderr, "Warning: Can't reach configuration directory.\n");
|
|
return -1;
|
|
}
|
|
free(filename);
|
|
|
|
return 0;
|
|
}
|
|
|
|
/*
|
|
* in the string s, substitute x by y
|
|
* NOTE: s is dynamically allocated and will be re-allocated by this function
|
|
*
|
|
* returns the number of substitutions
|
|
*/
|
|
int substitute(char **s, char *x, char *y) {
|
|
int result = 0;
|
|
char *pos;
|
|
char *temp;
|
|
|
|
while ((pos = strstr(*s, x))) { /* while we found something: substitute */
|
|
*pos = '\0';
|
|
asprintf(&temp, "%s%s%s", *s, y, &pos[strlen(x)]);
|
|
free(*s);
|
|
*s = temp;
|
|
|
|
result ++;
|
|
}
|
|
|
|
return result;
|
|
}
|
|
|
|
/*
|
|
* returns the date in the form of only digits, e.g. YYYYMMDDhhmmss
|
|
* the resulting string is dynamically allocated and should be deallocated
|
|
* with free().
|
|
*
|
|
* returns NULL on error
|
|
*/
|
|
char *util_digitstime(time_t *time) {
|
|
char *s = (char *)malloc(15);
|
|
int len;
|
|
|
|
s[0] = '\1';
|
|
len = strftime(s, 15, "%Y%m%d%H%M%S", localtime(time));
|
|
|
|
if (len == 0 && s[0] != '\0') {
|
|
return NULL;
|
|
} else {
|
|
return s;
|
|
}
|
|
}
|
|
|
|
/*
|
|
* returns a string where all occurences of the character c are removed
|
|
* from the input string s
|
|
* NOTE: caller has to free() the result
|
|
*/
|
|
char* stripchr(char* s, char c) {
|
|
int count = 1; /* trailing space */
|
|
char* temp;
|
|
char* result;
|
|
|
|
for (temp = s; *temp; temp++) {
|
|
if (*temp != c)
|
|
count++;
|
|
}
|
|
if ((result = (char*) malloc (count))) {
|
|
temp = result;
|
|
while (*s) {
|
|
if (*s != c) {
|
|
*temp = *s;
|
|
temp ++;
|
|
}
|
|
s++;
|
|
}
|
|
*temp = '\0';
|
|
}
|
|
|
|
return result;
|
|
}
|
|
|
|
/*
|
|
* returns the filename extension for the specified file / path
|
|
* or NULL on problems
|
|
*/
|
|
char* filename_extension(char* fn) {
|
|
char* result = strrchr(fn, '.');
|
|
if (result) {
|
|
result++;
|
|
if (strchr(result, '/')) {
|
|
result = NULL;
|
|
}
|
|
}
|
|
return result;
|
|
}
|
|
|
|
/*
|
|
* saves the current codeset globally for further calls to output_codeset_set()
|
|
* -> returns 0 on success, -1 otherwise (default: sets codeset to "UTF-8")
|
|
*/
|
|
static char* output_codeset = NULL; /* the saved codeset */
|
|
int output_codeset_save(void) {
|
|
if (!(output_codeset = bind_textdomain_codeset(PACKAGE, NULL))) {
|
|
output_codeset = strdup("UTF-8");
|
|
return -1;
|
|
} else {
|
|
output_codeset = strdup(output_codeset);
|
|
return 0;
|
|
}
|
|
}
|
|
|
|
/*
|
|
* sets the output codeset GNU gettext produces while translating messages
|
|
* if codeset == 0, a saved values will be used.
|
|
* (save with: output_codeset_save())
|
|
* -> returns 0 on success, -1 otherwise
|
|
*/
|
|
int output_codeset_set(char* codeset) {
|
|
if (!codeset)
|
|
codeset = output_codeset;
|
|
if (!bind_textdomain_codeset(PACKAGE, codeset)) {
|
|
fprintf(stderr, "Error setting gettext output codeset to %s.\n", codeset);
|
|
return -1;
|
|
}
|
|
return 0;
|
|
}
|
|
|