#include "cvs.h"
#include "closeout.h"
#include "setenv.h"
#include "strftime.h"
#include "xgethostname.h"
const char *program_name;
const char *program_path;
const char *cvs_cmd_name;
const char *global_session_id;
char *hostname;
char *server_hostname;
int use_editor = 1;
int use_cvsrc = 1;
int cvswrite = !CVSREAD_DFLT;
int really_quiet = 0;
int quiet = 0;
int trace = 0;
int noexec = 0;
int readonlyfs = 0;
int logoff = 0;
struct config *config;
mode_t cvsumask = UMASK_DFLT;
char *CurDir;
char *Editor = EDITOR_DFLT;
static char *tmpdir_cmdline;
const char *
get_cvs_tmp_dir (void)
{
const char *retval;
if (tmpdir_cmdline) retval = tmpdir_cmdline;
else if (config && config->TmpDir) retval = config->TmpDir;
else retval = get_system_temp_dir ();
if (!retval) retval = TMPDIR_DFLT;
if (!retval || !*retval) error (1, 0, "No temp dir specified.");
return retval;
}
List *root_directories = NULL;
static const struct cmd
{
const char *fullname;
const char *nick1;
const char *nick2;
int (*func) (int, char **);
unsigned long attr;
} cmds[] =
{
{ "add", "ad", "new", add, CVS_CMD_MODIFIES_REPOSITORY | CVS_CMD_USES_WORK_DIR },
{ "admin", "adm", "rcs", admin, CVS_CMD_MODIFIES_REPOSITORY | CVS_CMD_USES_WORK_DIR },
{ "annotate", "ann", NULL, annotate, CVS_CMD_USES_WORK_DIR },
{ "checkout", "co", "get", checkout, 0 },
{ "commit", "ci", "com", commit, CVS_CMD_MODIFIES_REPOSITORY | CVS_CMD_USES_WORK_DIR },
{ "diff", "di", "dif", diff, CVS_CMD_USES_WORK_DIR },
{ "edit", NULL, NULL, edit, CVS_CMD_MODIFIES_REPOSITORY | CVS_CMD_USES_WORK_DIR },
{ "editors", NULL, NULL, editors, CVS_CMD_USES_WORK_DIR },
{ "export", "exp", "ex", checkout, CVS_CMD_USES_WORK_DIR },
{ "history", "hi", "his", history, CVS_CMD_USES_WORK_DIR },
{ "import", "im", "imp", import, CVS_CMD_MODIFIES_REPOSITORY | CVS_CMD_USES_WORK_DIR | CVS_CMD_IGNORE_ADMROOT},
{ "init", NULL, NULL, init, CVS_CMD_MODIFIES_REPOSITORY },
#if defined (HAVE_KERBEROS) && defined (SERVER_SUPPORT)
{ "kserver", NULL, NULL, server, CVS_CMD_MODIFIES_REPOSITORY | CVS_CMD_USES_WORK_DIR },
#endif
{ "log", "lo", NULL, cvslog, CVS_CMD_USES_WORK_DIR },
#ifdef AUTH_CLIENT_SUPPORT
{ "login", "logon", "lgn", login, 0 },
{ "logout", NULL, NULL, logout, 0 },
#endif
{ "ls", "dir", "list", ls, 0 },
#if (defined(AUTH_SERVER_SUPPORT) || defined (HAVE_GSSAPI)) && defined(SERVER_SUPPORT)
{ "pserver", NULL, NULL, server, CVS_CMD_MODIFIES_REPOSITORY | CVS_CMD_USES_WORK_DIR },
#endif
{ "rannotate","rann", "ra", annotate, 0 },
{ "rdiff", "patch", "pa", patch, 0 },
{ "release", "re", "rel", release, CVS_CMD_MODIFIES_REPOSITORY },
{ "remove", "rm", "delete", cvsremove, CVS_CMD_MODIFIES_REPOSITORY | CVS_CMD_USES_WORK_DIR },
{ "rlog", "rl", NULL, cvslog, 0 },
{ "rls", "rdir", "rlist", ls, 0 },
{ "rtag", "rt", "rfreeze", cvstag, CVS_CMD_MODIFIES_REPOSITORY },
#ifdef SERVER_SUPPORT
{ "server", NULL, NULL, server, CVS_CMD_MODIFIES_REPOSITORY | CVS_CMD_USES_WORK_DIR },
#endif
{ "status", "st", "stat", cvsstatus, CVS_CMD_USES_WORK_DIR },
{ "tag", "ta", "freeze", cvstag, CVS_CMD_MODIFIES_REPOSITORY | CVS_CMD_USES_WORK_DIR },
{ "unedit", NULL, NULL, unedit, CVS_CMD_MODIFIES_REPOSITORY | CVS_CMD_USES_WORK_DIR },
{ "update", "up", "upd", update, CVS_CMD_USES_WORK_DIR },
{ "version", "ve", "ver", version, 0 },
{ "watch", NULL, NULL, watch, CVS_CMD_MODIFIES_REPOSITORY | CVS_CMD_USES_WORK_DIR },
{ "watchers", NULL, NULL, watchers, CVS_CMD_USES_WORK_DIR },
{ NULL, NULL, NULL, NULL, 0 },
};
static const char *const usg[] =
{
"Usage: %s [cvs-options] command [command-options-and-arguments]\n",
" where cvs-options are -q, -n, etc.\n",
" (specify --help-options for a list of options)\n",
" where command is add, admin, etc.\n",
" (specify --help-commands for a list of commands\n",
" or --help-synonyms for a list of command synonyms)\n",
" where command-options-and-arguments depend on the specific command\n",
" (specify -H followed by a command name for command-specific help)\n",
" Specify --help to receive this message\n",
"\n",
"The Concurrent Versions System (CVS) is a tool for version control.\n",
"For CVS updates and additional information, see\n",
" the CVS home page at http://www.nongnu.org/cvs/ or\n",
" the CVSNT home page at http://www.cvsnt.org/\n",
NULL,
};
static const char *const cmd_usage[] =
{
"CVS commands are:\n",
" add Add a new file/directory to the repository\n",
" admin Administration front end for rcs\n",
" annotate Show last revision where each line was modified\n",
" checkout Checkout sources for editing\n",
" commit Check files into the repository\n",
" diff Show differences between revisions\n",
" edit Get ready to edit a watched file\n",
" editors See who is editing a watched file\n",
" export Export sources from CVS, similar to checkout\n",
" history Show repository access history\n",
" import Import sources into CVS, using vendor branches\n",
" init Create a CVS repository if it doesn't exist\n",
#if defined (HAVE_KERBEROS) && defined (SERVER_SUPPORT)
" kserver Kerberos server mode\n",
#endif
" log Print out history information for files\n",
#ifdef AUTH_CLIENT_SUPPORT
" login Prompt for password for authenticating server\n",
" logout Removes entry in .cvspass for remote repository\n",
#endif
" ls List files available from CVS\n",
#if (defined(AUTH_SERVER_SUPPORT) || defined (HAVE_GSSAPI)) && defined(SERVER_SUPPORT)
" pserver Password server mode\n",
#endif
" rannotate Show last revision where each line of module was modified\n",
" rdiff Create 'patch' format diffs between releases\n",
" release Indicate that a Module is no longer in use\n",
" remove Remove an entry from the repository\n",
" rlog Print out history information for a module\n",
" rls List files in a module\n",
" rtag Add a symbolic tag to a module\n",
#ifdef SERVER_SUPPORT
" server Server mode\n",
#endif
" status Display status information on checked out files\n",
" tag Add a symbolic tag to checked out version of files\n",
" unedit Undo an edit command\n",
" update Bring work tree in sync with repository\n",
" version Show current CVS version(s)\n",
" watch Set watches\n",
" watchers See who is watching a file\n",
"(Specify the --help option for a list of other help options)\n",
NULL,
};
static const char *const opt_usage[] =
{
"CVS global options (specified before the command name) are:\n",
" -H Displays usage information for command.\n",
" -Q Cause CVS to be really quiet.\n",
" -q Cause CVS to be somewhat quiet.\n",
" -r Make checked-out files read-only.\n",
" -w Make checked-out files read-write (default).\n",
" -n Do not execute anything that will change the disk.\n",
" -t Show trace of program execution (repeat for more\n",
" verbosity) -- try with -n.\n",
" -R Assume repository is read-only, such as CDROM\n",
" -v CVS version and copyright.\n",
" -T tmpdir Use 'tmpdir' for temporary files.\n",
" -e editor Use 'editor' for editing log information.\n",
" -d CVS_root Overrides $CVSROOT as the root of the CVS tree.\n",
" -f Do not use the ~/.cvsrc file.\n",
#ifdef CLIENT_SUPPORT
" -z # Request compression level '#' for net traffic.\n",
#ifdef ENCRYPTION
" -x Encrypt all net traffic.\n",
#endif
" -a Authenticate all net traffic.\n",
#endif
" -s VAR=VAL Set CVS user variable.\n",
"(Specify the --help option for a list of other help options)\n",
NULL
};
static int
set_root_directory (Node *p, void *ignored)
{
if (current_parsed_root == NULL && p->data != NULL)
{
current_parsed_root = p->data;
original_parsed_root = current_parsed_root;
return 1;
}
return 0;
}
static const char * const*
cmd_synonyms (void)
{
char ** synonyms;
char ** line;
const struct cmd *c = &cmds[0];
int numcmds = 3;
while (c->fullname != NULL)
{
numcmds++;
c++;
}
synonyms = xnmalloc (numcmds, sizeof(char *));
line = synonyms;
*line++ = "CVS command synonyms are:\n";
for (c = &cmds[0]; c->fullname != NULL; c++)
{
if (c->nick1 || c->nick2)
{
*line = Xasprintf (" %-12s %s %s\n", c->fullname,
c->nick1 ? c->nick1 : "",
c->nick2 ? c->nick2 : "");
line++;
}
}
*line++ = "(Specify the --help option for a list of other help options)\n";
*line = NULL;
return (const char * const*) synonyms;
}
unsigned long int
lookup_command_attribute (const char *cmd_name)
{
const struct cmd *cm;
for (cm = cmds; cm->fullname; cm++)
{
if (strcmp (cmd_name, cm->fullname) == 0)
break;
}
if (!cm->fullname)
error (1, 0, "unknown command: %s", cmd_name);
return cm->attr;
}
#ifndef DONT_USE_SIGNALS
static RETSIGTYPE main_cleanup (int) __attribute__ ((__noreturn__));
#endif
static RETSIGTYPE
main_cleanup (int sig)
{
#ifndef DONT_USE_SIGNALS
const char *name;
char temp[10];
switch (sig)
{
#ifdef SIGABRT
case SIGABRT:
name = "abort";
break;
#endif
#ifdef SIGHUP
case SIGHUP:
name = "hangup";
break;
#endif
#ifdef SIGINT
case SIGINT:
name = "interrupt";
break;
#endif
#ifdef SIGQUIT
case SIGQUIT:
name = "quit";
break;
#endif
#ifdef SIGPIPE
case SIGPIPE:
name = "broken pipe";
break;
#endif
#ifdef SIGTERM
case SIGTERM:
name = "termination";
break;
#endif
default:
sprintf (temp, "%d", sig);
name = temp;
break;
}
error (1, 0, "received %s signal", name);
exit (EXIT_FAILURE);
#endif
}
extern char *gConfigPath;
enum {RANDOM_BYTES = 8};
enum {COMMITID_RAW_SIZE = (sizeof(time_t) + RANDOM_BYTES)};
static char const alphabet[62] =
"0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ";
static unsigned int
divide_by (unsigned char buf[COMMITID_RAW_SIZE], unsigned int d)
{
unsigned int carry = 0;
int i;
for (i = 0; i < COMMITID_RAW_SIZE; i++)
{
unsigned int byte = buf[i];
unsigned int dividend = (carry << CHAR_BIT) + byte;
buf[i] = dividend / d;
carry = dividend % d;
}
return carry;
}
static void
convert (char const input[COMMITID_RAW_SIZE], char *output)
{
static char const zero[COMMITID_RAW_SIZE] = { 0, };
unsigned char buf[COMMITID_RAW_SIZE];
size_t o = 0;
memcpy (buf, input, COMMITID_RAW_SIZE);
while (memcmp (buf, zero, COMMITID_RAW_SIZE) != 0)
output[o++] = alphabet[divide_by (buf, sizeof alphabet)];
if (! o)
output[o++] = '0';
output[o] = '\0';
}
int
main (int argc, char **argv)
{
cvsroot_t *CVSroot_parsed = NULL;
bool cvsroot_update_env = true;
char *cp, *end;
const struct cmd *cm;
int c, err = 0;
int free_Editor = 0;
int help = 0;
static const char short_options[] = "+QqrwtnRvb:T:e:d:Hfz:s:xa";
static struct option long_options[] =
{
{"help", 0, NULL, 'H'},
{"version", 0, NULL, 'v'},
{"help-commands", 0, NULL, 1},
{"help-synonyms", 0, NULL, 2},
{"help-options", 0, NULL, 4},
#ifdef SERVER_SUPPORT
{"allow-root", required_argument, NULL, 3},
#endif
{0, 0, 0, 0}
};
int option_index = 0;
#ifdef SYSTEM_INITIALIZE
SYSTEM_INITIALIZE (&argc, &argv);
#endif
#ifdef SYSTEM_CLEANUP
cleanup_register (SYSTEM_CLEANUP);
#endif
#ifdef HAVE_TZSET
tzset ();
#endif
program_path = xstrdup (argv[0]);
#ifdef ARGV0_NOT_PROGRAM_NAME
program_name = "cvs";
#else
program_name = last_component (argv[0]);
#endif
if ((cp = getenv (EDITOR1_ENV)) != NULL)
Editor = cp;
else if ((cp = getenv (EDITOR2_ENV)) != NULL)
Editor = cp;
else if ((cp = getenv (EDITOR3_ENV)) != NULL)
Editor = cp;
if (getenv (CVSREAD_ENV) != NULL)
cvswrite = 0;
if (getenv (CVSREADONLYFS_ENV) != NULL) {
readonlyfs = 1;
logoff = 1;
}
optind = 0;
opterr = 0;
while ((c = getopt_long
(argc, argv, short_options, long_options, &option_index))
!= EOF)
{
if (c == 'f')
use_cvsrc = 0;
}
#ifdef SERVER_SUPPORT
if (optind < argc
&& (false
# if defined (AUTH_SERVER_SUPPORT) || defined (HAVE_GSSAPI)
|| !strcmp (argv[optind], "pserver")
# endif
# ifdef HAVE_KERBEROS
|| !strcmp (argv[optind], "kserver")
# endif
|| !strcmp (argv[optind], "server")))
{
use_cvsrc = 0;
cvs_cmd_name = argv[optind];
parseServerOptions (argc - optind, argv + optind);
}
#endif
if (use_cvsrc)
read_cvsrc (&argc, &argv, "cvs");
optind = 0;
opterr = 1;
while ((c = getopt_long
(argc, argv, short_options, long_options, &option_index))
!= EOF)
{
switch (c)
{
case 1:
usage (cmd_usage);
break;
case 2:
usage (cmd_synonyms());
break;
case 4:
usage (opt_usage);
break;
#ifdef SERVER_SUPPORT
case 3:
root_allow_add (optarg, gConfigPath);
break;
#endif
case 'Q':
really_quiet = 1;
case 'q':
quiet = 1;
break;
case 'r':
cvswrite = 0;
break;
case 'w':
cvswrite = 1;
break;
case 't':
trace++;
break;
case 'R':
readonlyfs = -1;
logoff = 1;
break;
case 'n':
noexec = 1;
logoff = 1;
break;
case 'v':
(void) fputs ("\n", stdout);
version (0, NULL);
(void) fputs ("\n", stdout);
(void) fputs ("\
Copyright (C) 2005 Free Software Foundation, Inc.\n\
\n\
Senior active maintainers include Larry Jones, Derek R. Price,\n\
and Mark D. Baushke. Please see the AUTHORS and README files from the CVS\n\
distribution kit for a complete list of contributors and copyrights.\n",
stdout);
(void) fputs ("\n", stdout);
(void) fputs ("CVS may be copied only under the terms of the GNU General Public License,\n", stdout);
(void) fputs ("a copy of which can be found with the CVS distribution kit.\n", stdout);
(void) fputs ("\n", stdout);
(void) fputs ("Specify the --help option for further information about CVS\n", stdout);
exit (0);
break;
case 'b':
break;
case 'T':
if (tmpdir_cmdline) free (tmpdir_cmdline);
tmpdir_cmdline = xstrdup (optarg);
break;
case 'e':
if (free_Editor) free (Editor);
Editor = xstrdup (optarg);
free_Editor = 1;
break;
case 'd':
if (CVSroot_cmdline != NULL)
free (CVSroot_cmdline);
CVSroot_cmdline = xstrdup (optarg);
break;
case 'H':
help = 1;
break;
case 'f':
use_cvsrc = 0;
break;
case 'z':
#ifdef CLIENT_SUPPORT
gzip_level = strtol (optarg, &end, 10);
if (*end != '\0' || gzip_level < 0 || gzip_level > 9)
error (1, 0,
"gzip compression level must be between 0 and 9");
#endif
break;
case 's':
variable_set (optarg);
break;
case 'x':
#ifdef CLIENT_SUPPORT
cvsencrypt = 1;
#endif
break;
case 'a':
#ifdef CLIENT_SUPPORT
cvsauthenticate = 1;
#endif
break;
case '?':
default:
usage (usg);
}
}
argc -= optind;
argv += optind;
if (argc < 1)
usage (usg);
if (readonlyfs && !really_quiet) {
error (0, 0,
"WARNING: Read-only repository access mode selected via `cvs -R'.\n\
Using this option to access a repository which some users write to may\n\
cause intermittent sandbox corruption.");
}
{
char buf[COMMITID_RAW_SIZE] = { 0, };
char out[COMMITID_RAW_SIZE * 2];
ssize_t len = 0;
time_t rightnow = time (NULL);
char *startrand = buf + sizeof (time_t);
unsigned char *p = (unsigned char *) startrand;
size_t randbytes = RANDOM_BYTES;
int flags = O_RDONLY;
int fd;
#ifdef O_NOCTTY
flags |= O_NOCTTY;
#endif
if (rightnow != (time_t)-1)
while (rightnow > 0) {
*--p = rightnow % (UCHAR_MAX + 1);
rightnow /= UCHAR_MAX + 1;
}
else {
randbytes = COMMITID_RAW_SIZE;
startrand = buf;
}
fd = open ("/dev/urandom", flags);
if (fd >= 0) {
len = read (fd, startrand, randbytes);
close (fd);
}
if (len <= 0) {
long int pid = (long int)getpid ();
p = (unsigned char *) (startrand + sizeof (pid));
while (pid > 0) {
*--p = pid % (UCHAR_MAX + 1);
pid /= UCHAR_MAX + 1;
}
}
convert(buf, out);
global_session_id = strdup (out);
}
TRACE (TRACE_FUNCTION, "main: Session ID is %s", global_session_id);
cvs_cmd_name = argv[0];
for (cm = cmds; cm->fullname; cm++)
{
if (cm->nick1 && !strcmp (cvs_cmd_name, cm->nick1))
break;
if (cm->nick2 && !strcmp (cvs_cmd_name, cm->nick2))
break;
if (!strcmp (cvs_cmd_name, cm->fullname))
break;
}
if (!cm->fullname)
{
fprintf (stderr, "Unknown command: `%s'\n\n", cvs_cmd_name);
usage (cmd_usage);
}
else
cvs_cmd_name = cm->fullname;
if (help)
{
argc = -1;
err = (*(cm->func)) (argc, argv);
}
else
{
short int lock_cleanup_setup = 0;
if ((cp = getenv (CVSUMASK_ENV)) != NULL)
{
cvsumask = strtol (cp, &end, 8) & 0777;
if (*end != '\0')
error (1, errno, "invalid umask value in %s (%s)",
CVSUMASK_ENV, cp);
}
hostname = xgethostname ();
if (!hostname)
{
error (0, errno,
"xgethostname () returned NULL, using \"localhost\"");
hostname = xstrdup ("localhost");
}
server_hostname = xstrdup (hostname);
#ifdef SERVER_SUPPORT
# ifdef HAVE_KERBEROS
if (strcmp (cvs_cmd_name, "kserver") == 0)
{
kserver_authenticate_connection ();
cvs_cmd_name = "server";
}
# endif
# if defined (AUTH_SERVER_SUPPORT) || defined (HAVE_GSSAPI)
if (strcmp (cvs_cmd_name, "pserver") == 0)
{
pserver_authenticate_connection ();
cvs_cmd_name = "server";
}
# endif
#endif
server_active = strcmp (cvs_cmd_name, "server") == 0;
#ifdef SERVER_SUPPORT
if (server_active)
{
CurDir = xstrdup ("<remote>");
cleanup_register (server_cleanup);
}
else
#endif
{
cleanup_register (close_stdout);
CurDir = xgetcwd ();
if (CurDir == NULL)
error (1, errno, "cannot get working directory");
}
{
char *val;
val = Xasprintf ("%ld", (long) getpid ());
setenv (CVS_PID_ENV, val, 1);
free (val);
}
signals_register (main_cleanup);
#ifdef KLUDGE_FOR_WNT_TESTSUITE
(void) setvbuf (stdout, NULL, _IONBF, 0);
(void) setvbuf (stderr, NULL, _IONBF, 0);
#endif
if (use_cvsrc)
read_cvsrc (&argc, &argv, cvs_cmd_name);
if (!server_active)
{
if (CVSroot_cmdline)
{
if (!(CVSroot_parsed = parse_cvsroot (CVSroot_cmdline)))
error (1, 0, "Bad CVSROOT: `%s'.", CVSroot_cmdline);
}
if (!CVSroot_parsed
&& !(cm->attr & CVS_CMD_IGNORE_ADMROOT)
)
CVSroot_parsed = Name_Root (NULL, NULL);
if (!CVSroot_parsed)
{
char *tmp = getenv (CVSROOT_ENV);
if (tmp)
{
if (!(CVSroot_parsed = parse_cvsroot (tmp)))
error (1, 0, "Bad CVSROOT: `%s'.", tmp);
cvsroot_update_env = false;
}
}
#ifdef CVSROOT_DFLT
if (!CVSroot_parsed)
{
if (!(CVSroot_parsed = parse_cvsroot (CVSROOT_DFLT)))
error (1, 0, "Bad CVSROOT: `%s'.", CVSROOT_DFLT);
}
#endif
if (!CVSroot_parsed)
{
error (0, 0,
"No CVSROOT specified! Please use the `-d' option");
error (1, 0,
"or set the %s environment variable.", CVSROOT_ENV);
}
}
assert (root_directories == NULL);
root_directories = getlist ();
if (CVSroot_parsed)
{
Node *n;
n = getnode ();
n->type = NT_UNKNOWN;
n->key = xstrdup (CVSroot_parsed->original);
n->data = CVSroot_parsed;
if (addnode (root_directories, n))
error (1, 0, "cannot add initial CVSROOT %s", n->key);
}
assert (current_parsed_root == NULL);
while (server_active ||
walklist (root_directories, set_root_directory, NULL))
{
if (!server_active)
{
TRACE (TRACE_FUNCTION,
"main loop with CVSROOT=%s",
current_parsed_root ? current_parsed_root->directory
: "(null)");
if (!current_parsed_root->isremote)
{
char *path;
int save_errno;
path = Xasprintf ("%s/%s", current_parsed_root->directory,
CVSROOTADM);
if (!isaccessible (path, R_OK | X_OK))
{
save_errno = errno;
if (strcmp (cvs_cmd_name, "init"))
error (1, save_errno, "%s", path);
}
free (path);
}
if (cvsroot_update_env)
setenv (CVSROOT_ENV, current_parsed_root->original, 1);
}
if (!server_active && !current_parsed_root->isremote)
{
if (config) free_config (config);
config = parse_config (current_parsed_root->directory, NULL);
push_env_temp_dir ();
}
#ifdef CLIENT_SUPPORT
if (current_parsed_root != NULL && current_parsed_root->isremote)
{
if (dirs_sent_to_server != NULL)
dellist (&dirs_sent_to_server);
dirs_sent_to_server = getlist ();
}
#endif
if (
#ifdef SERVER_SUPPORT
server_active ||
#endif
(
#ifdef CLIENT_SUPPORT
!current_parsed_root->isremote &&
#endif
!lock_cleanup_setup))
{
cleanup_register (Lock_Cleanup);
lock_cleanup_setup = 1;
}
err = (*(cm->func)) (argc, argv);
if (!server_active)
{
Node *n = findnode (root_directories,
original_parsed_root->original);
assert (n != NULL);
assert (n->data != NULL);
n->data = NULL;
current_parsed_root = NULL;
}
if (server_active)
break;
}
dellist (&root_directories);
}
root_allow_free ();
exit (err ? EXIT_FAILURE : 0);
return 0;
}
char *
Make_Date (const char *rawdate)
{
struct timespec t;
if (!get_date (&t, rawdate, NULL))
error (1, 0, "Can't parse date/time: `%s'", rawdate);
return date_from_time_t (t.tv_sec);
}
void
parse_tagdate (char **tag, char **date, const char *input)
{
char *p;
TRACE (TRACE_FUNCTION, "parse_tagdate (%s, %s, %s)",
*tag ? *tag : "(null)", *date ? *date : "(null)",
input);
if ((p = strchr (input, ':')))
{
if (p - input)
{
if (*tag) free (*tag);
*tag = xmalloc (p - input + 1);
strncpy (*tag, input, p - input);
(*tag)[p - input] = '\0';
}
if (*++p)
{
if (*date) free (*date);
*date = Make_Date (p);
}
}
else if (strlen (input))
{
if (*tag) free (*tag);
*tag = xstrdup (input);
}
TRACE (TRACE_DATA, "parse_tagdate: got tag = `%s', date = `%s'",
*tag ? *tag : "(null)", *date ? *date : "(null)");
}
char *
date_from_time_t (time_t unixtime)
{
struct tm *ftm;
char date[MAXDATELEN];
char *ret;
ftm = gmtime (&unixtime);
if (ftm == NULL)
ftm = localtime (&unixtime);
(void) sprintf (date, DATEFORM,
ftm->tm_year + (ftm->tm_year < 100 ? 0 : 1900),
ftm->tm_mon + 1, ftm->tm_mday, ftm->tm_hour,
ftm->tm_min, ftm->tm_sec);
ret = xstrdup (date);
return ret;
}
void
date_to_internet (char *dest, const char *source)
{
struct tm date;
date_to_tm (&date, source);
tm_to_internet (dest, &date);
}
void
date_to_tm (struct tm *dest, const char *source)
{
if (sscanf (source, SDATEFORM,
&dest->tm_year, &dest->tm_mon, &dest->tm_mday,
&dest->tm_hour, &dest->tm_min, &dest->tm_sec)
!= 6)
error (0, 0, "internal error: bad date %s", source);
if (dest->tm_year > 100)
dest->tm_year -= 1900;
dest->tm_mon -= 1;
}
void
tm_to_internet (char *dest, const struct tm *source)
{
static const char *const month_names[] =
{"Jan", "Feb", "Mar", "Apr", "May", "Jun",
"Jul", "Aug", "Sep", "Oct", "Nov", "Dec"};
sprintf (dest, "%d %s %d %02d:%02d:%02d -0000", source->tm_mday,
source->tm_mon < 0 || source->tm_mon > 11
? "???" : month_names[source->tm_mon],
source->tm_year + 1900, source->tm_hour, source->tm_min,
source->tm_sec);
}
static char *
format_time_t (time_t unixtime)
{
static char buf[sizeof ("yyyy-mm-dd HH:MM:SS -HHMM")];
struct tm ltm = *(localtime (&unixtime));
if (!my_strftime (buf, sizeof (buf), "%Y-%m-%d %H:%M:%S %z", <m, 0, 0))
return NULL;
return xstrdup (buf);
}
char *
gmformat_time_t (time_t unixtime)
{
static char buf[sizeof ("yyyy-mm-dd HH:MM:SS -HHMM")];
struct tm ltm = *(gmtime (&unixtime));
if (!my_strftime (buf, sizeof (buf), "%Y-%m-%d %H:%M:%S %z", <m, 0, 0))
return NULL;
return xstrdup (buf);
}
char *
format_date_alloc (char *datestr)
{
struct timespec t;
char *buf;
TRACE (TRACE_FUNCTION, "format_date (%s)", datestr);
if (!get_date (&t, datestr, NULL))
{
error (0, 0, "Can't parse date/time: `%s'.", datestr);
goto as_is;
}
if ((buf = format_time_t (t.tv_sec)) == NULL)
{
error (0, 0, "Unable to reformat date `%s'.", datestr);
goto as_is;
}
return buf;
as_is:
return xstrdup (datestr);
}
void
usage (register const char *const *cpp)
{
(void) fprintf (stderr, *cpp++, program_name, cvs_cmd_name);
for (; *cpp; cpp++)
(void) fprintf (stderr, *cpp);
exit (EXIT_FAILURE);
}