Import cvs-1.9.24 since it came quite soon after the snapshot that was

imported a few days ago.  There is very little difference, except that
the remote protocol apparently supports wrappers and -k options better.
This commit is contained in:
Peter Wemm 1998-01-30 15:31:47 +00:00
parent 57e58c3aa7
commit 7b9d411405
Notes: svn2git 2020-12-20 02:59:44 +00:00
svn path=/vendor/cvs/dist/; revision=32896
14 changed files with 438 additions and 19 deletions

View file

@ -1,3 +1,17 @@
Wed Jan 28 23:09:39 1998 Jim Kingdon <kingdon@harvey.cyclic.com>
* cvs.texinfo (Excluding directories): Add index entry for "!".
28 Jan 1998 Karl Fogel and Jim Kingdon
* cvsclient.texi (Requests, Responses): document
"wrapper-sendme-rcsOptions" and "Wrapper-rcsOption".
Tue Jan 27 18:37:37 1998 Ian Lance Taylor <ian@cygnus.com>
* cvs.texinfo (Excluding directories): New node, documenting how
to exclude directories using ! in an alias module.
Sun Jan 18 18:23:02 1998 Jim Kingdon <kingdon@harvey.cyclic.com>
* cvsclient.texi (Requests): Add Kopt request.

View file

@ -10513,6 +10513,7 @@ a file @file{sfile}.
* Alias modules:: The simplest kind of module
* Regular modules::
* Ampersand modules::
* Excluding directories:: Excluding directories from a module
* Module options:: Regular and ampersand modules can take options
@end menu
@ -10685,6 +10686,25 @@ a future release of @sc{cvs}.
@c should put in a few words about why you would use one or
@c the other in various situations.
@node Excluding directories
@appendixsubsec Excluding directories
@cindex excluding directories, in modules file
@cindex !, in modules file
An alias module may exclude particular directories from
other modules by using an exclamation mark (@samp{!})
before the name of each directory to be excluded.
For example, if the modules file contains:
@example
exmodule -a first-dir !first-dir/sdir
@end example
then checking out the module @samp{exmodule} will check
out everything in @samp{first-dir} except any files in
the subdirectory @samp{first-dir/sdir}.
@node Module options
@appendixsubsec Module options
@cindex options, in modules file

View file

@ -1081,6 +1081,11 @@ Availability of this request in the server indicates to the client that
it may compress files sent to the server, regardless of whether the
client actually uses this request.
@item wrapper-sendme-rcsOptions \n
Response expected: yes.
Request that the server transmit mappings from filenames to keyword
expansion modes in @code{Wrapper-rcsOption} responses.
@item @var{other-request} @var{text} \n
Response expected: yes.
Any unrecognized request expects a response, and does not
@ -1372,6 +1377,17 @@ argument to a @code{co} request (for example, if the modules file
contains the @samp{-d} option, it will be the directory specified with
@samp{-d}, not the name of the module).
@item Wrapper-rcsOption @var{pattern} -k '@var{option}' \n
Transmit to the client a filename pattern which implies a certain
keyword expansion mode. The @var{pattern} is a wildcard pattern (for
example, @samp{*.exe}. The @var{option} is @samp{b} for binary, and so
on. Note that although the syntax happens to resemble the syntax in
certain CVS configuration files, it is more constrained; there must be
exactly one space between @var{pattern} and @samp{-k} and exactly one
space between @samp{-k} and @samp{'}, and no string is permitted in
place of @samp{-k} (extensions should be done with new responses, not by
extending this one, for graceful handling of @code{Valid-responses}).
@item M @var{text} \n
A one-line message for the user.

View file

@ -1,3 +1,45 @@
Thu Jan 29 00:01:05 1998 Jim Kingdon <kingdon@harvey.cyclic.com>
* Version 1.9.24.
* sanity.sh (multibranch2): File file2 and tests multibranch2-13
through multibranch2-15 test a slightly different case than the
rest of multibranch2.
* mkmodules.c (cvswrappers_contents): Rewrite. The text didn't
describe -k and had various other problems.
28 Jan 1998 Karl Fogel and Jim Kingdon
New feature to let server tell client about wrappers.
* client.h (struct response): Add comment about args being
'\0' terminated when passed to handle_* functions.
* client.c (start_server): send "wrapper-sendme-rcsOptions" to
server iff supported.
(responses): new response "Wrapper-rcsOption"; allows the server
to send certain lines from its cvswrappers file.
(handle_wrapper_rcs_option): new func, handles "Wrapper-rcsOption"
response from server.
* server.c (serve_wrapper_sendme_rcs_options): new func, sends
server side CVSROOT/cvswrappers rcs option lines to client.
(requests): new request "wrapper-sendme-rcsOptions"; if received,
we know we can send "Wrapper-rcsOption..." to the client.
* wrapper.c (wrap_unparse_rcs_options): new func; repeated calls
step down the wrapper list returning rcs option entries, but
repackaged as cvswrappers lines.
(wrap_setup): new guard variable `wrap_setup_already_done'; if
this function has run already, just return having done nothing.
Add comment concerning environment variable.
* cvs.h: declare wrap_unparse_rcs_options().
Tue Jan 27 18:27:19 1998 Ian Lance Taylor <ian@cygnus.com>
* rtag.c (rtag_dirproc): Call ignore_directory, and skip the
directory if it returns true.
* sanity.sh (modules4): New set of tests to test some aspects of
excluding directories in the modules file, including the above
patch.
Thu Jan 22 10:05:55 1998 Jim Kingdon <kingdon@harvey.cyclic.com>
* server.c (serve_kopt): Check for length of arg. Based on

View file

@ -128,6 +128,7 @@ static void handle_clear_sticky PROTO((char *, int));
static void handle_set_checkin_prog PROTO((char *, int));
static void handle_set_update_prog PROTO((char *, int));
static void handle_module_expansion PROTO((char *, int));
static void handle_wrapper_rcs_option PROTO((char *, int));
static void handle_m PROTO((char *, int));
static void handle_e PROTO((char *, int));
static void handle_f PROTO((char *, int));
@ -2884,6 +2885,44 @@ client_nonexpanded_setup ()
{
send_a_repository ("", CVSroot_directory, "");
}
/* Receive a cvswrappers line from the server; it must be a line
containing an RCS option (e.g., "*.exe -k 'b'").
Note that this doesn't try to handle -t/-f options (which are a
whole separate issue which noone has thought much about, as far
as I know).
We need to know the keyword expansion mode so we know whether to
read the file in text or binary mode. */
static void
handle_wrapper_rcs_option (args, len)
char *args;
int len;
{
char *p;
/* Enforce the notes in cvsclient.texi about how the response is not
as free-form as it looks. */
p = strchr (args, ' ');
if (p == NULL)
goto error;
if (*++p != '-'
|| *++p != 'k'
|| *++p != ' '
|| *++p != '\'')
goto error;
if (strchr (p, '\'') == NULL)
goto error;
/* Add server-side cvswrappers line to our wrapper list. */
wrap_add (args, 0);
return;
error:
error (0, errno, "protocol error: ignoring invalid wrappers %s", args);
}
static void
handle_m (args, len)
@ -3073,6 +3112,9 @@ struct response responses[] =
RSP_LINE("Notified", handle_notified, response_type_normal, rs_optional),
RSP_LINE("Module-expansion", handle_module_expansion, response_type_normal,
rs_optional),
RSP_LINE("Wrapper-rcsOption", handle_wrapper_rcs_option,
response_type_normal,
rs_optional),
RSP_LINE("M", handle_m, response_type_normal, rs_essential),
RSP_LINE("Mbinary", handle_mbinary, response_type_normal, rs_optional),
RSP_LINE("E", handle_e, response_type_normal, rs_essential),
@ -4161,6 +4203,27 @@ the :server: access method is not supported by this port of CVS");
}
}
/* Find out about server-side cvswrappers. An extra network
turnaround for cvs import seems to be unavoidable, unless we
want to add some kind of client-side place to configure which
filenames imply binary. For cvs add, we could avoid the
problem by keeping a copy of the wrappers in CVSADM (the main
reason to bother would be so we could make add work without
contacting the server, I suspect). */
if ((strcmp (command_name, "import") == 0)
|| (strcmp (command_name, "add") == 0))
{
if (supported_request ("wrapper-sendme-rcsOptions"))
{
int err;
send_to_server ("wrapper-sendme-rcsOptions\012", 0);
err = get_server_responses ();
if (err != 0)
error (err, 0, "error reading from server");
}
}
if (cvsencrypt)
{
#ifdef ENCRYPTION

View file

@ -139,6 +139,8 @@ struct response
* Function to carry out the response. ARGS is the text of the
* command after name and, if present, a single space, have been
* stripped off. The function can scribble into ARGS if it wants.
* Note that although LEN is given, ARGS is also guaranteed to be
* '\0' terminated.
*/
void (*func) PROTO((char *args, int len));

View file

@ -794,6 +794,9 @@ void wrap_fromcvs_process_file PROTO ((const char *fileName));
void wrap_add_file PROTO((const char *file,int temp));
void wrap_add PROTO((char *line,int temp));
void wrap_send PROTO ((void));
#if defined(SERVER_SUPPORT) || defined(CLIENT_SUPPORT)
void wrap_unparse_rcs_options PROTO ((char **, int));
#endif /* SERVER_SUPPORT || CLIENT_SUPPORT */
/* Pathname expansion */
char *expand_path PROTO((char *name, char *file, int line));

View file

@ -204,15 +204,15 @@ static const char *const checkoutlist_contents[] = {
};
static const char *const cvswrappers_contents[] = {
"# This file describes wrappers and other binary files to CVS.\n",
"# This file affects handling of files based on their names.\n",
"#\n",
"# Wrappers are the concept where directories of files are to be\n",
"# treated as a single file. The intended use is to wrap up a wrapper\n",
"# into a single tar such that the tar archive can be treated as a\n",
"# single binary file in CVS.\n",
"# The -t/-f options allow one to treat directories of files\n",
"# as a single file, or to transform a file in other ways on\n",
"# its way in and out of CVS.\n",
"#\n",
"# To solve the problem effectively, it was also necessary to be able to\n",
"# prevent rcsmerge from merging these files.\n",
"# The -m option specifies whether CVS attempts to merge files.\n",
"#\n",
"# The -k option specifies keyword expansion (e.g. -kb for binary).\n",
"#\n",
"# Format of wrapper file ($CVSROOT/CVSROOT/cvswrappers or .cvswrappers)\n",
"#\n",
@ -222,10 +222,11 @@ static const char *const cvswrappers_contents[] = {
"# -f from cvs filter value: path to filter\n",
"# -t to cvs filter value: path to filter\n",
"# -m update methodology value: MERGE or COPY\n",
"# -k expansion mode value: b, o, kkv, &c\n",
"#\n",
"# and value is a single-quote delimited value.\n",
"#\n",
"# For example:\n",
"#*.gif -k 'b'\n",
NULL
};

View file

@ -739,6 +739,14 @@ rtag_dirproc (callerdat, dir, repos, update_dir, entries)
char *update_dir;
List *entries;
{
if (ignore_directory (update_dir))
{
/* print the warm fuzzy message */
if (!quiet)
error (0, 0, "Ignoring %s", update_dir);
return R_SKIP_ALL;
}
if (!quiet)
error (0, 0, "%s %s", delete_flag ? "Untagging" : "Tagging",
update_dir);

View file

@ -552,7 +552,8 @@ if test x"$*" = x; then
tests="${tests} rdiff death death2 branches"
tests="${tests} rcslib multibranch import importb join join2 join3"
tests="${tests} new newb conflicts conflicts2 conflicts3"
tests="${tests} modules modules2 modules3 mflag editor errmsg1 errmsg2"
tests="${tests} modules modules2 modules3 modules4"
tests="${tests} mflag editor errmsg1 errmsg2"
tests="${tests} devcom devcom2 devcom3 watch4"
tests="${tests} ignore binfiles binfiles2 mcopy binwrap binwrap2"
tests="${tests} binwrap3 mwrap info config"
@ -3670,7 +3671,7 @@ add
# \----->branch
#
# /----->branch1
# --->bp---->trunk multibranch
# --->bp---->trunk multibranch, multibranch2
# \----->branch2
#
# --->bp1----->bp2---->trunk join3
@ -5704,6 +5705,106 @@ done"
rm -rf ${CVSROOT_DIRNAME}/second-dir
;;
modules4)
# Some tests using the modules file with aliases that
# exclude particular directories.
mkdir 1; cd 1
dotest modules4-1 "${testcvs} -q co -l ." ''
mkdir first-dir
dotest modules4-2 "${testcvs} add first-dir" \
"Directory ${TESTDIR}/cvsroot/first-dir added to the repository"
cd first-dir
mkdir subdir
dotest modules4-3 "${testcvs} add subdir" \
"Directory ${TESTDIR}/cvsroot/first-dir/subdir added to the repository"
echo file1 > file1
dotest modules4-4 "${testcvs} add file1" \
"${PROG}"' [a-z]*: scheduling file `file1'\'' for addition
'"${PROG}"' [a-z]*: use .'"${PROG}"' commit. to add this file permanently'
echo file2 > subdir/file2
dotest modules4-5 "${testcvs} add subdir/file2" \
"${PROG}"' [a-z]*: scheduling file `subdir/file2'\'' for addition
'"${PROG}"' [a-z]*: use .'"${PROG}"' commit. to add this file permanently'
dotest modules4-6 "${testcvs} -q ci -m add-it" \
"RCS file: ${TESTDIR}/cvsroot/first-dir/file1,v
done
Checking in file1;
${TESTDIR}/cvsroot/first-dir/file1,v <-- file1
initial revision: 1\.1
done
RCS file: ${TESTDIR}/cvsroot/first-dir/subdir/file2,v
done
Checking in subdir/file2;
${TESTDIR}/cvsroot/first-dir/subdir/file2,v <-- file2
initial revision: 1\.1
done"
cd ..
dotest modules4-7 "${testcvs} -q update -d CVSROOT" \
"U CVSROOT${DOTSTAR}"
cd CVSROOT
cat >modules <<EOF
all -a first-dir
some -a !first-dir/subdir first-dir
EOF
dotest modules4-8 "${testcvs} -q ci -m add-modules" \
"Checking in modules;
${TESTDIR}/cvsroot/CVSROOT/modules,v <-- modules
new revision: 1\.[0-9]*; previous revision: 1\.[0-9]*
done
${PROG} [a-z]*: Rebuilding administrative file database"
cd ..
cd ..
mkdir 2; cd 2
dotest modules4-9 "${testcvs} -q co all" \
"U first-dir/file1
U first-dir/subdir/file2"
rm -r first-dir
dotest modules4-10 "${testcvs} -q co some" "U first-dir/file1"
dotest_fail modules4-11 "test -d first-dir/subdir" ''
rm -r first-dir
cd ..
rm -r 2
dotest modules4-12 "${testcvs} rtag tag some" \
"${PROG} [a-z]*: Tagging first-dir
${PROG} [a-z]*: Ignoring first-dir/subdir"
cd 1/first-dir/subdir
dotest modules4-13 "${testcvs} log file2" "
RCS file: ${TESTDIR}/cvsroot/first-dir/subdir/file2,v
Working file: file2
head: 1\.1
branch:
locks: strict
access list:
symbolic names:
keyword substitution: kv
total revisions: 1; selected revisions: 1
description:
----------------------------
revision 1\.1
date: [0-9/]* [0-9:]*; author: ${username}; state: Exp;
add-it
============================================================================="
cd ../../..
rm -r 1
rm -rf ${CVSROOT_DIRNAME}/first-dir
;;
mflag)
for message in '' ' ' '
' ' test' ; do
@ -10214,28 +10315,44 @@ done"
cd first-dir
echo trunk-1 >file1
dotest multibranch2-3 "${testcvs} add file1" \
echo trunk-1 >file2
dotest multibranch2-3 "${testcvs} add file1 file2" \
"${PROG} [a-z]*: scheduling file .file1. for addition
${PROG} [a-z]*: use .${PROG} commit. to add this file permanently"
${PROG} [a-z]*: scheduling file .file2. for addition
${PROG} [a-z]*: use .${PROG} commit. to add these files permanently"
dotest multibranch2-4 "${testcvs} -q ci -m add" \
"RCS file: ${TESTDIR}/cvsroot/first-dir/file1,v
done
Checking in file1;
${TESTDIR}/cvsroot/first-dir/file1,v <-- file1
initial revision: 1\.1
done
RCS file: ${TESTDIR}/cvsroot/first-dir/file2,v
done
Checking in file2;
${TESTDIR}/cvsroot/first-dir/file2,v <-- file2
initial revision: 1\.1
done"
dotest multibranch2-5 "${testcvs} -q tag -b A" "T file1"
dotest multibranch2-6 "${testcvs} -q tag -b B" "T file1"
dotest multibranch2-5 "${testcvs} -q tag -b A" "T file1
T file2"
dotest multibranch2-6 "${testcvs} -q tag -b B" "T file1
T file2"
dotest multibranch2-7 "${testcvs} -q update -r B" ''
echo branch-B >file1
echo branch-B >file2
dotest multibranch2-8 "${testcvs} -q ci -m modify-on-B" \
"Checking in file1;
${TESTDIR}/cvsroot/first-dir/file1,v <-- file1
new revision: 1\.1\.4\.1; previous revision: 1\.1
done
Checking in file2;
${TESTDIR}/cvsroot/first-dir/file2,v <-- file2
new revision: 1\.1\.4\.1; previous revision: 1\.1
done"
dotest multibranch2-9 "${testcvs} -q update -r A" '[UP] file1'
dotest multibranch2-9 "${testcvs} -q update -r A" '[UP] file1
[UP] file2'
echo branch-A >file1
# When using cvs-1.9.20, this commit gets a failed assertion in rcs.c.
dotest multibranch2-10 "${testcvs} -q ci -m modify-on-A" \
@ -10244,7 +10361,7 @@ ${TESTDIR}/cvsroot/first-dir/file1,v <-- file1
new revision: 1\.1\.2\.1; previous revision: 1\.1
done"
dotest multibranch2-11 "${testcvs} -q log" \
dotest multibranch2-11 "${testcvs} -q log file1" \
"
RCS file: ${TESTDIR}/cvsroot/first-dir/file1,v
Working file: file1
@ -10274,7 +10391,7 @@ modify-on-A
============================================================================="
# This one is more concise.
dotest multibranch2-12 "${testcvs} -q log -r1.1" \
dotest multibranch2-12 "${testcvs} -q log -r1.1 file1" \
"
RCS file: ${TESTDIR}/cvsroot/first-dir/file1,v
Working file: file1
@ -10295,6 +10412,23 @@ branches: 1\.1\.2; 1\.1\.4;
add
============================================================================="
# OK, try very much the same thing except we run update -j to
# bring the changes from B to A. Probably tests many of the
# same code paths but might as well keep it separate, I guess.
dotest multibranch2-13 "${testcvs} -q update -r B" "[UP] file1
[UP] file2"
dotest multibranch2-14 "${testcvs} -q update -r A -j B file2" \
"[UP] file2
RCS file: ${TESTDIR}/cvsroot/first-dir/file2,v
retrieving revision 1.1
retrieving revision 1.1.4.1
Merging differences between 1.1 and 1.1.4.1 into file2"
dotest multibranch2-15 "${testcvs} -q ci -m commit-on-A file2" \
"Checking in file2;
${TESTDIR}/cvsroot/first-dir/file2,v <-- file2
new revision: 1\.1\.2\.1; previous revision: 1\.1
done"
cd ../..
rm -r 1
rm -rf ${CVSROOT_DIRNAME}/first-dir

View file

@ -3779,6 +3779,38 @@ serve_gzip_stream (arg)
buf_from_net = compress_buffer_initialize (buf_from_net, 1, level,
buf_from_net->memory_error);
}
/* Tell the client about RCS options set in CVSROOT/cvswrappers. */
static void
serve_wrapper_sendme_rcs_options (arg)
char *arg;
{
/* Actually, this is kind of sdrawkcab-ssa: the client wants
* verbatim lines from a cvswrappers file, but the server has
* already parsed the cvswrappers file into the wrap_list struct.
* Therefore, the server loops over wrap_list, unparsing each
* entry before sending it.
*/
char *wrapper_line = NULL;
wrap_setup ();
for (wrap_unparse_rcs_options (&wrapper_line, 1);
wrapper_line;
wrap_unparse_rcs_options (&wrapper_line, 0))
{
buf_output0 (buf_to_net, "Wrapper-rcsOption ");
buf_output0 (buf_to_net, wrapper_line);
buf_output0 (buf_to_net, "\012");;
free (wrapper_line);
}
buf_output0 (buf_to_net, "ok\012");
/* The client is waiting for us, so we better send the data now. */
buf_flush (buf_to_net, 1);
}
static void
serve_ignore (arg)
@ -4049,6 +4081,9 @@ struct request requests[] =
REQ_LINE("Argumentx", serve_argumentx, rq_essential),
REQ_LINE("Global_option", serve_global_option, rq_optional),
REQ_LINE("Gzip-stream", serve_gzip_stream, rq_optional),
REQ_LINE("wrapper-sendme-rcsOptions",
serve_wrapper_sendme_rcs_options,
rq_optional),
REQ_LINE("Set", serve_set, rq_optional),
#ifdef ENCRYPTION
# ifdef HAVE_KERBEROS

View file

@ -12,7 +12,7 @@
#include "cvs.h"
char *version_string = "\nConcurrent Versions System (CVS) 1.9.23";
char *version_string = "\nConcurrent Versions System (CVS) 1.9.24";
#ifdef CLIENT_SUPPORT
#ifdef SERVER_SUPPORT

View file

@ -84,8 +84,17 @@ void wrap_restore_saved PROTO((void));
void wrap_setup()
{
/* FIXME-reentrancy: if we do a multithreaded server, will need to
move this to a per-connection data structure, or better yet
think about a cleaner solution. */
static int wrap_setup_already_done = 0;
char *homedir;
if (wrap_setup_already_done != 0)
return;
else
wrap_setup_already_done = 1;
#ifdef CLIENT_SUPPORT
if (!client_active)
#endif
@ -122,6 +131,23 @@ void wrap_setup()
free (file);
}
/* FIXME: calling wrap_add() below implies that the CVSWRAPPERS
* environment variable contains exactly one "wrapper" -- a line
* of the form
*
* FILENAME_PATTERN FLAG OPTS [ FLAG OPTS ...]
*
* This may disagree with the documentation, which states:
*
* `$CVSWRAPPERS'
* A whitespace-separated list of file name patterns that CVS
* should treat as wrappers. *Note Wrappers::.
*
* Does this mean the environment variable can hold multiple
* wrappers lines? If so, a single call to wrap_add() is
* insufficient.
*/
/* Then add entries found in CVSWRAPPERS environment variable. */
wrap_add (getenv (WRAPPER_ENV), 0);
}
@ -159,6 +185,61 @@ wrap_send ()
}
#endif /* CLIENT_SUPPORT */
#if defined(SERVER_SUPPORT) || defined(CLIENT_SUPPORT)
/* Output wrapper entries in the format of cvswrappers lines.
*
* This is useful when one side of a client/server connection wants to
* send its wrappers to the other; since the receiving side would like
* to use wrap_add() to incorporate the wrapper, it's best if the
* entry arrives in this format.
*
* The entries are stored in `line', which is allocated here. Caller
* can free() it.
*
* If first_call_p is nonzero, then start afresh. */
void
wrap_unparse_rcs_options (line, first_call_p)
char **line;
int first_call_p;
{
/* FIXME-reentrancy: we should design a reentrant interface, like
a callback which gets handed each wrapper (a multithreaded
server being the most concrete reason for this, but the
non-reentrant interface is fairly unnecessary/ugly). */
static int i;
if (first_call_p)
i = 0;
for (; i < wrap_count + wrap_tempcount; ++i)
{
if (wrap_list[i]->rcsOption != NULL)
{
*line = xmalloc (strlen (wrap_list[i]->wildCard)
+ strlen ("\t")
+ strlen (" -k '")
+ strlen (wrap_list[i]->rcsOption)
+ strlen ("'")
+ 1); /* leave room for '\0' */
strcpy (*line, wrap_list[i]->wildCard);
strcat (*line, " -k '");
strcat (*line, wrap_list[i]->rcsOption);
strcat (*line, "'");
/* We're going to miss the increment because we return, so
do it by hand. */
++i;
return;
}
}
*line = NULL;
return;
}
#endif /* SERVER_SUPPORT || CLIENT_SUPPORT */
/*
* Open a file and read lines, feeding each line to a line parser. Arrange
* for keeping a temporary list of wrappers at the end, if the "temp"

View file

@ -91,4 +91,4 @@ from `pcl-cvs.texinfo'.
--
#ident "@(#)cvs/contrib/pcl-cvs:$Name: $Id$"
#ident "@(#)cvs/contrib/pcl-cvs:$Name: $Id: INSTALL,v 1.2 1996/04/15 06:33:16 kfogel Exp $"