All of lore.kernel.org
 help / color / mirror / Atom feed
* [PATCH 00/17] pull: term-utils changes with emphasis on last(1)
@ 2013-08-27 18:06 Sami Kerola
  2013-08-27 18:06 ` [PATCH 02/17] wall: send message also to sessions opened by user 'sleeper' Sami Kerola
                   ` (16 more replies)
  0 siblings, 17 replies; 25+ messages in thread
From: Sami Kerola @ 2013-08-27 18:06 UTC (permalink / raw)
  To: util-linux; +Cc: kerolasa

Notice that the first patch is skipped intentionally.  I sent it already
yesterday, but it is in my git tree if someone prefers inspecting the
work that way.

The patches 02 to 08 are fairly trivial changes, and commit messages
should be enough.

The 09 is perhaps more interesting.  I were unable to send with write(1)
a message from one of my own terminals to other when mesg(1) had enabled. 
Because I felt that is unexpeceted the 09 will change how write(1) will
determine messaging permission.  As with any permissions change it would
be great to get this change properly scrutinised.

>From patch 10 onwards the changes are mostly outcome of earlier request
in the maillist.

http://www.spinics.net/lists/util-linux-ng/msg08227.html

To make time format parsing user friendly I decided to plunder code from
systemd.  It is a pity there does not seem to be libtime for all sorts of
time related format parsing, conversions, etc high level needs to avoid
each project doing these on their own.

The last command got couple new options, --since and --time-format, which
hopefully make the utility more useful.  The patch 15 aims to make
contributors happier by removing bunch of global variables, and simplify
function argument passing.

Looking these changes I realized there is still at least one last(1) thing
missing.  The last(1) test coverage is zero.  If I don't need to rewrite
all/most of these patches I could concentrate the tests next.

Meanwhile all sort of comments, proposals, and corrections are welcome.


The following changes since commit ef2c9e3f5e51cf13ec9cf6738d4f1d695ee32a6f:

  tests: minor changes to make tests more portable (2013-08-26 13:48:40 +0200)

are available in the git repository at:

  git://github.com/kerolasa/lelux-utiliteetit.git 2013wk34

for you to fetch changes up to 69d9bdd65371edf37bb20308582f05fe2c095195:

  docs: add --time-format to last(1) manual page (2013-08-27 18:15:00 +0100)

----------------------------------------------------------------
Sami Kerola (17):
      build-sys: pylibmount will not work when python3 is present
      wall: send message also to sessions opened by user 'sleeper'
      last: fix typo
      wall: sync usage() with howto-usage-function.txt
      mesg: sync usage() with howto-usage-function.txt
      wall: line wrap at column 79 also when line has tab chars
      docs: add --present to last(1) manual page
      docs: add note to wall(1) about sessions which will not get message
      write: change determination can user write to a terminal
      lib/time-util: copy time parsing functions from systemd
      last: parse easy to use time formats
      last: add --since time spec option
      docs: add --since and time option formats to last(1) manual
      last: fix --present option logic error
      last: use configuration struct
      last: add --time-format with iso-8601 format
      docs: add --time-format to last(1) manual page

 configure.ac              |   6 +
 include/Makemodule.am     |   1 +
 include/time-util.h       |  55 +++++++
 lib/Makemodule.am         |   1 +
 lib/time-util.c           | 388 ++++++++++++++++++++++++++++++++++++++++++++++
 login-utils/Makemodule.am |   1 +
 login-utils/last.1        |  63 +++++++-
 login-utils/last.c        | 359 ++++++++++++++++++++++++++++--------------
 term-utils/mesg.c         |  15 +-
 term-utils/wall.1         |   6 +
 term-utils/wall.c         |  28 ++--
 term-utils/write.c        |   9 +-
 12 files changed, 784 insertions(+), 148 deletions(-)
 create mode 100644 include/time-util.h
 create mode 100644 lib/time-util.c



^ permalink raw reply	[flat|nested] 25+ messages in thread

* [PATCH 02/17] wall: send message also to sessions opened by user 'sleeper'
  2013-08-27 18:06 [PATCH 00/17] pull: term-utils changes with emphasis on last(1) Sami Kerola
@ 2013-08-27 18:06 ` Sami Kerola
  2013-08-27 18:06 ` [PATCH 03/17] last: fix typo Sami Kerola
                   ` (15 subsequent siblings)
  16 siblings, 0 replies; 25+ messages in thread
From: Sami Kerola @ 2013-08-27 18:06 UTC (permalink / raw)
  To: util-linux; +Cc: kerolasa

Ignoring an user by name, and not telling about it in manual page, is
unexpected.

Signed-off-by: Sami Kerola <kerolasa@iki.fi>
---
 term-utils/wall.c | 5 +----
 1 file changed, 1 insertion(+), 4 deletions(-)

diff --git a/term-utils/wall.c b/term-utils/wall.c
index 2d87e47..98dc5c7 100644
--- a/term-utils/wall.c
+++ b/term-utils/wall.c
@@ -69,7 +69,6 @@
 #include "fileutils.h"
 #include "closestream.h"
 
-#define	IGNOREUSER	"sleeper"
 #define WRITE_TIME_OUT	300		/* in seconds */
 
 /* Function prototypes */
@@ -157,9 +156,7 @@ int main(int argc, char **argv)
 	iov.iov_base = mbuf;
 	iov.iov_len = mbufsize;
 	while((utmpptr = getutent())) {
-		if (!utmpptr->ut_name[0] ||
-		    !strncmp(utmpptr->ut_name, IGNOREUSER,
-			     sizeof(utmpptr->ut_name)))
+		if (!utmpptr->ut_name[0])
 			continue;
 #ifdef USER_PROCESS
 		if (utmpptr->ut_type != USER_PROCESS)
-- 
1.8.4


^ permalink raw reply related	[flat|nested] 25+ messages in thread

* [PATCH 03/17] last: fix typo
  2013-08-27 18:06 [PATCH 00/17] pull: term-utils changes with emphasis on last(1) Sami Kerola
  2013-08-27 18:06 ` [PATCH 02/17] wall: send message also to sessions opened by user 'sleeper' Sami Kerola
@ 2013-08-27 18:06 ` Sami Kerola
  2013-08-27 18:06 ` [PATCH 04/17] wall: sync usage() with howto-usage-function.txt Sami Kerola
                   ` (14 subsequent siblings)
  16 siblings, 0 replies; 25+ messages in thread
From: Sami Kerola @ 2013-08-27 18:06 UTC (permalink / raw)
  To: util-linux; +Cc: kerolasa

Signed-off-by: Sami Kerola <kerolasa@iki.fi>
---
 login-utils/last.c | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/login-utils/last.c b/login-utils/last.c
index 8d944a8..3367038 100644
--- a/login-utils/last.c
+++ b/login-utils/last.c
@@ -433,7 +433,7 @@ static void __attribute__((__noreturn__)) usage(FILE *out)
 	fputs(USAGE_SEPARATOR, out);
 	fputs(USAGE_HELP, out);
 	fputs(USAGE_VERSION, out);
-	fprintf(out, USAGE_MAN_TAIL("last(1))"));
+	fprintf(out, USAGE_MAN_TAIL("last(1)"));
 
 	exit(out == stderr ? EXIT_FAILURE : EXIT_SUCCESS);
 }
-- 
1.8.4


^ permalink raw reply related	[flat|nested] 25+ messages in thread

* [PATCH 04/17] wall: sync usage() with howto-usage-function.txt
  2013-08-27 18:06 [PATCH 00/17] pull: term-utils changes with emphasis on last(1) Sami Kerola
  2013-08-27 18:06 ` [PATCH 02/17] wall: send message also to sessions opened by user 'sleeper' Sami Kerola
  2013-08-27 18:06 ` [PATCH 03/17] last: fix typo Sami Kerola
@ 2013-08-27 18:06 ` Sami Kerola
  2013-08-27 18:06 ` [PATCH 05/17] mesg: " Sami Kerola
                   ` (13 subsequent siblings)
  16 siblings, 0 replies; 25+ messages in thread
From: Sami Kerola @ 2013-08-27 18:06 UTC (permalink / raw)
  To: util-linux; +Cc: kerolasa

Signed-off-by: Sami Kerola <kerolasa@iki.fi>
---
 term-utils/wall.c | 21 ++++++++++-----------
 1 file changed, 10 insertions(+), 11 deletions(-)

diff --git a/term-utils/wall.c b/term-utils/wall.c
index 98dc5c7..451b5d3 100644
--- a/term-utils/wall.c
+++ b/term-utils/wall.c
@@ -77,16 +77,16 @@ static char *makemsg(char *fname, char **mvec, int mvecsz,
 
 static void __attribute__((__noreturn__)) usage(FILE *out)
 {
-	fputs(_("\nUsage:\n"), out);
+	fputs(USAGE_HEADER, out);
 	fprintf(out,
-		_(" %s [options] [<file> | <message>]\n"),
-		program_invocation_short_name);
-
-	fputs(_("\nOptions:\n"), out);
-	fputs(_(" -n, --nobanner          do not print banner, works only for root\n"
-		" -t, --timeout <timeout> write timeout in seconds\n"
-		" -V, --version           output version information and exit\n"
-		" -h, --help              display this help and exit\n\n"), out);
+	      _(" %s [options] [<file> | <message>]\n"), program_invocation_short_name);
+	fputs(USAGE_OPTIONS, out);
+	fputs(_(" -n, --nobanner          do not print banner, works only for root\n"), out);
+	fputs(_(" -t, --timeout <timeout> write timeout in seconds\n"), out);
+	fputs(USAGE_SEPARATOR, out);
+	fputs(USAGE_HELP, out);
+	fputs(USAGE_VERSION, out);
+	fprintf(out, USAGE_MAN_TAIL("wall(1)"));
 
 	exit(out == stderr ? EXIT_FAILURE : EXIT_SUCCESS);
 }
@@ -132,8 +132,7 @@ int main(int argc, char **argv)
 				errx(EXIT_FAILURE, _("invalid timeout argument: %s"), optarg);
 			break;
 		case 'V':
-			printf(_("%s from %s\n"), program_invocation_short_name,
-						  PACKAGE_STRING);
+			printf(UTIL_LINUX_VERSION);
 			exit(EXIT_SUCCESS);
 		case 'h':
 			usage(stdout);
-- 
1.8.4


^ permalink raw reply related	[flat|nested] 25+ messages in thread

* [PATCH 05/17] mesg: sync usage() with howto-usage-function.txt
  2013-08-27 18:06 [PATCH 00/17] pull: term-utils changes with emphasis on last(1) Sami Kerola
                   ` (2 preceding siblings ...)
  2013-08-27 18:06 ` [PATCH 04/17] wall: sync usage() with howto-usage-function.txt Sami Kerola
@ 2013-08-27 18:06 ` Sami Kerola
  2013-08-27 18:06 ` [PATCH 06/17] wall: line wrap at column 79 also when line has tab chars Sami Kerola
                   ` (12 subsequent siblings)
  16 siblings, 0 replies; 25+ messages in thread
From: Sami Kerola @ 2013-08-27 18:06 UTC (permalink / raw)
  To: util-linux; +Cc: kerolasa

Signed-off-by: Sami Kerola <kerolasa@iki.fi>
---
 term-utils/mesg.c | 15 +++++++--------
 1 file changed, 7 insertions(+), 8 deletions(-)

diff --git a/term-utils/mesg.c b/term-utils/mesg.c
index 430236f..097fb9c 100644
--- a/term-utils/mesg.c
+++ b/term-utils/mesg.c
@@ -68,16 +68,16 @@
 
 static void __attribute__ ((__noreturn__)) usage(FILE * out)
 {
-	fputs(_("\nUsage:\n"), out);
+	fputs(USAGE_HEADER, out);
 	/* TRANSLATORS: this program uses for y and n rpmatch(3),
 	 * which means they can be translated.  */
 	fprintf(out,
 	      _(" %s [options] [y | n]\n"), program_invocation_short_name);
-
-	fputs(_("\nOptions:\n"), out);
-	fputs(_(" -v, --verbose      explain what is being done\n"
-		" -V, --version      output version information and exit\n"
-		" -h, --help         output help screen and exit\n\n"), out);
+	fputs(USAGE_OPTIONS, out);
+	fputs(_(" -v, --verbose  explain what is being done\n"), out);
+	fputs(USAGE_HELP, out);
+	fputs(USAGE_VERSION, out);
+	fprintf(out, USAGE_MAN_TAIL("mesg(1)"));
 
 	exit(out == stderr ? MESG_EXIT_FAILURE : EXIT_SUCCESS);
 }
@@ -106,8 +106,7 @@ int main(int argc, char *argv[])
 			verbose = TRUE;
 			break;
 		case 'V':
-			printf(_("%s from %s\n"), program_invocation_short_name,
-			PACKAGE_STRING);
+			printf(UTIL_LINUX_VERSION);
 			exit(EXIT_SUCCESS);
 		case 'h':
 			usage(stdout);
-- 
1.8.4


^ permalink raw reply related	[flat|nested] 25+ messages in thread

* [PATCH 06/17] wall: line wrap at column 79 also when line has tab chars
  2013-08-27 18:06 [PATCH 00/17] pull: term-utils changes with emphasis on last(1) Sami Kerola
                   ` (3 preceding siblings ...)
  2013-08-27 18:06 ` [PATCH 05/17] mesg: " Sami Kerola
@ 2013-08-27 18:06 ` Sami Kerola
  2013-08-27 18:06 ` [PATCH 07/17] docs: add --present to last(1) manual page Sami Kerola
                   ` (11 subsequent siblings)
  16 siblings, 0 replies; 25+ messages in thread
From: Sami Kerola @ 2013-08-27 18:06 UTC (permalink / raw)
  To: util-linux; +Cc: kerolasa

Earlier tabs caused random ragged right indentation, because the tab
length was assumed to be 1 char which is not the case most of the time.

Signed-off-by: Sami Kerola <kerolasa@iki.fi>
---
 term-utils/wall.c | 2 ++
 1 file changed, 2 insertions(+)

diff --git a/term-utils/wall.c b/term-utils/wall.c
index 451b5d3..fbea7e4 100644
--- a/term-utils/wall.c
+++ b/term-utils/wall.c
@@ -281,6 +281,8 @@ static char *makemsg(char *fname, char **mvec, int mvecsz,
 					putc('\n', fp);
 					cnt = 0;
 				}
+				if (ch == '\t')
+					cnt += (7 - (cnt % 8));
 				if (ch != '\n')
 					carefulputc(ch, fp, '^');
 			}
-- 
1.8.4


^ permalink raw reply related	[flat|nested] 25+ messages in thread

* [PATCH 07/17] docs: add --present to last(1) manual page
  2013-08-27 18:06 [PATCH 00/17] pull: term-utils changes with emphasis on last(1) Sami Kerola
                   ` (4 preceding siblings ...)
  2013-08-27 18:06 ` [PATCH 06/17] wall: line wrap at column 79 also when line has tab chars Sami Kerola
@ 2013-08-27 18:06 ` Sami Kerola
  2013-08-27 18:06 ` [PATCH 08/17] docs: add note to wall(1) about sessions which will not get message Sami Kerola
                   ` (10 subsequent siblings)
  16 siblings, 0 replies; 25+ messages in thread
From: Sami Kerola @ 2013-08-27 18:06 UTC (permalink / raw)
  To: util-linux; +Cc: kerolasa

I forgot to update manual after writing this functionality.

Signed-off-by: Sami Kerola <kerolasa@iki.fi>
---
 login-utils/last.1 | 6 ++++++
 1 file changed, 6 insertions(+)

diff --git a/login-utils/last.1 b/login-utils/last.1
index b33af5c..d348645 100644
--- a/login-utils/last.1
+++ b/login-utils/last.1
@@ -95,6 +95,12 @@ specify that time with
 .B \-\-until
 and look for "still logged in".
 .TP
+\fB\-p\fR, \fB\-\-present\fR \fIYYYYMMDDHHMMSS\fR
+Display users who were present at the specified time.  This option is
+similar with
+.BR \-\-until ,
+without printing any other lines than the matching sessions.
+.TP
 \fB\-F\fR, \fB\-\-fulltimes\fR
 Print full login and logout times and dates.
 .TP
-- 
1.8.4


^ permalink raw reply related	[flat|nested] 25+ messages in thread

* [PATCH 08/17] docs: add note to wall(1) about sessions which will not get message
  2013-08-27 18:06 [PATCH 00/17] pull: term-utils changes with emphasis on last(1) Sami Kerola
                   ` (5 preceding siblings ...)
  2013-08-27 18:06 ` [PATCH 07/17] docs: add --present to last(1) manual page Sami Kerola
@ 2013-08-27 18:06 ` Sami Kerola
  2013-08-27 18:06 ` [PATCH 09/17] write: change determination can user write to a terminal Sami Kerola
                   ` (9 subsequent siblings)
  16 siblings, 0 replies; 25+ messages in thread
From: Sami Kerola @ 2013-08-27 18:06 UTC (permalink / raw)
  To: util-linux; +Cc: kerolasa

Sessions that has in utmp data ut_line begining with ':' are skipped.
This is done to avoid write errors to devices such as '/dev/:0'.  For
example wdm is reported to to generate such utmp line.

Perhaps in future this code should be revalidated.  If an utmp data
contains broken device lines it might be best inform user with a error
message, rather than skipping silently suspicious device entries.

Signed-off-by: Sami Kerola <kerolasa@iki.fi>
---
 term-utils/wall.1 | 6 ++++++
 1 file changed, 6 insertions(+)

diff --git a/term-utils/wall.1 b/term-utils/wall.1
index 9bf3b26..bb56625 100644
--- a/term-utils/wall.1
+++ b/term-utils/wall.1
@@ -73,6 +73,12 @@ Output version and exit.
 .TP
 \fB\-h\fR, \fB\-\-help\fR
 Output help and exit.
+.SH NOTES
+Some sessions, such as wdm, that has in beginning of
+.BR utmp (5)
+ut_type data ':' character will not get message from
+.BR wall .
+This is done to avoid write errors.
 .SH SEE ALSO
 .BR mesg (1),
 .BR talk (1),
-- 
1.8.4


^ permalink raw reply related	[flat|nested] 25+ messages in thread

* [PATCH 09/17] write: change determination can user write to a terminal
  2013-08-27 18:06 [PATCH 00/17] pull: term-utils changes with emphasis on last(1) Sami Kerola
                   ` (6 preceding siblings ...)
  2013-08-27 18:06 ` [PATCH 08/17] docs: add note to wall(1) about sessions which will not get message Sami Kerola
@ 2013-08-27 18:06 ` Sami Kerola
  2013-08-27 18:06 ` [PATCH 10/17] lib/time-util: copy time parsing functions from systemd Sami Kerola
                   ` (8 subsequent siblings)
  16 siblings, 0 replies; 25+ messages in thread
From: Sami Kerola @ 2013-08-27 18:06 UTC (permalink / raw)
  To: util-linux; +Cc: kerolasa

Earlier test failed when user had write permission, but was not in
special group that owns terminal devices, usually tty.

This made write(1) to fail for root, if the root did not happen to be in
tty group.  In this commit root is granted ot write to anyone, even if
they have mesg(1) turned off.  For an user who is trying to write to own
other session the group write bit is significant only for whether mesg(1)
are enabled.

Signed-off-by: Sami Kerola <kerolasa@iki.fi>
---
 term-utils/write.c | 9 +++++----
 1 file changed, 5 insertions(+), 4 deletions(-)

diff --git a/term-utils/write.c b/term-utils/write.c
index cb3b044..1738694 100644
--- a/term-utils/write.c
+++ b/term-utils/write.c
@@ -73,7 +73,7 @@ static void __attribute__ ((__noreturn__)) done(int);
 int term_chk(char *, int *, time_t *, int);
 int utmp_chk(char *, char *);
 
-static gid_t myegid;
+static gid_t root_access;
 
 static void __attribute__ ((__noreturn__)) usage(FILE * out)
 {
@@ -120,7 +120,7 @@ int main(int argc, char **argv)
 			usage(stderr);
 		}
 
-	myegid = getegid();
+	root_access = !getegid();
 
 	/* check that sender has write enabled */
 	if (isatty(fileno(stdin)))
@@ -299,8 +299,9 @@ int term_chk(char *tty, int *msgsokP, time_t * atimeP, int showerror)
 		return 1;
 	}
 
-	/* group write bit and group ownership */
-	*msgsokP = (s.st_mode & (S_IWRITE >> 3)) && myegid == s.st_gid;
+	*msgsokP = !access(path, W_OK);
+	if (!root_access && *msgsokP)
+		*msgsokP = s.st_mode & S_IWGRP;
 	*atimeP = s.st_atime;
 	return 0;
 }
-- 
1.8.4


^ permalink raw reply related	[flat|nested] 25+ messages in thread

* [PATCH 10/17] lib/time-util: copy time parsing functions from systemd
  2013-08-27 18:06 [PATCH 00/17] pull: term-utils changes with emphasis on last(1) Sami Kerola
                   ` (7 preceding siblings ...)
  2013-08-27 18:06 ` [PATCH 09/17] write: change determination can user write to a terminal Sami Kerola
@ 2013-08-27 18:06 ` Sami Kerola
  2013-08-29 10:11   ` Karel Zak
  2013-08-27 18:06 ` [PATCH 11/17] last: parse easy to use time formats Sami Kerola
                   ` (7 subsequent siblings)
  16 siblings, 1 reply; 25+ messages in thread
From: Sami Kerola @ 2013-08-27 18:06 UTC (permalink / raw)
  To: util-linux; +Cc: kerolasa, Lennart Poettering

The functions are copied nearly as-is.  Coding style has been modified to
match with util-linux project, while the functionality remains untouched.

CC: Lennart Poettering <lennart@poettering.net>
Signed-off-by: Sami Kerola <kerolasa@iki.fi>
---
 include/Makemodule.am |   1 +
 include/time-util.h   |  55 +++++++
 lib/Makemodule.am     |   1 +
 lib/time-util.c       | 388 ++++++++++++++++++++++++++++++++++++++++++++++++++
 4 files changed, 445 insertions(+)
 create mode 100644 include/time-util.h
 create mode 100644 lib/time-util.c

diff --git a/include/Makemodule.am b/include/Makemodule.am
index 7ba4593..33276e1 100644
--- a/include/Makemodule.am
+++ b/include/Makemodule.am
@@ -40,6 +40,7 @@ dist_noinst_HEADERS += \
 	include/swapheader.h \
 	include/sysfs.h \
 	include/timer.h \
+	include/time-util.h
 	include/tt.h \
 	include/ttyutils.h \
 	include/wholedisk.h \
diff --git a/include/time-util.h b/include/time-util.h
new file mode 100644
index 0000000..bcae613
--- /dev/null
+++ b/include/time-util.h
@@ -0,0 +1,55 @@
+/***
+  First set of functions in this file are part of systemd, and were
+  copied to util-linux at August 2013.
+
+  Copyright 2010 Lennart Poettering
+
+  systemd is free software; you can redistribute it and/or modify it
+  under the terms of the GNU Lesser General Public License as published by
+  the Free Software Foundation; either version 2.1 of the License, or
+  (at your option) any later version.
+
+  systemd 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
+  Lesser General Public License for more details.
+
+  You should have received a copy of the GNU Lesser General Public License
+  along with systemd; If not, see <http://www.gnu.org/licenses/>.
+***/
+#ifndef UTIL_LINUX_TIME_UTIL_H
+#define UTIL_LINUX_TIME_UTIL_H
+
+#include <stdio.h>
+#include <inttypes.h>
+
+typedef uint64_t usec_t;
+typedef uint64_t nsec_t;
+
+#define MSEC_PER_SEC  1000ULL
+#define USEC_PER_SEC  1000000ULL
+#define USEC_PER_MSEC 1000ULL
+#define NSEC_PER_SEC  1000000000ULL
+#define NSEC_PER_MSEC 1000000ULL
+#define NSEC_PER_USEC 1000ULL
+
+#define USEC_PER_MINUTE	(60ULL*USEC_PER_SEC)
+#define NSEC_PER_MINUTE	(60ULL*NSEC_PER_SEC)
+#define USEC_PER_HOUR	(60ULL*USEC_PER_MINUTE)
+#define NSEC_PER_HOUR	(60ULL*NSEC_PER_MINUTE)
+#define USEC_PER_DAY	(24ULL*USEC_PER_HOUR)
+#define NSEC_PER_DAY	(24ULL*NSEC_PER_HOUR)
+#define USEC_PER_WEEK	(7ULL*USEC_PER_DAY)
+#define NSEC_PER_WEEK	(7ULL*NSEC_PER_DAY)
+#define USEC_PER_MONTH	(2629800ULL*USEC_PER_SEC)
+#define NSEC_PER_MONTH	(2629800ULL*NSEC_PER_SEC)
+#define USEC_PER_YEAR	(31557600ULL*USEC_PER_SEC)
+#define NSEC_PER_YEAR	(31557600ULL*NSEC_PER_SEC)
+
+#define FORMAT_TIMESTAMP_MAX ((4*4+1)+11+9+4+1)	/* weekdays can be unicode */
+#define FORMAT_TIMESTAMP_RELATIVE_MAX 256
+#define FORMAT_TIMESPAN_MAX 64
+
+int parse_timestamp(const char *t, usec_t *usec);
+
+#endif /* UTIL_LINUX_TIME_UTIL_H */
diff --git a/lib/Makemodule.am b/lib/Makemodule.am
index 6c3a1b0..32d46ba 100644
--- a/lib/Makemodule.am
+++ b/lib/Makemodule.am
@@ -23,6 +23,7 @@ libcommon_la_SOURCES = \
 	lib/sysfs.c \
 	lib/tt.c \
 	lib/wholedisk.c \
+	lib/time-util.c \
 	lib/ttyutils.c \
 	lib/xgetpass.c \
 	lib/exec_shell.c
diff --git a/lib/time-util.c b/lib/time-util.c
new file mode 100644
index 0000000..c8fcc2a
--- /dev/null
+++ b/lib/time-util.c
@@ -0,0 +1,388 @@
+/***
+  First set of functions in this file are part of systemd, and were
+  copied to util-linux at August 2013.
+
+  Copyright 2010 Lennart Poettering
+
+  systemd is free software; you can redistribute it and/or modify it
+  under the terms of the GNU Lesser General Public License as published by
+  the Free Software Foundation; either version 2.1 of the License, or
+  (at your option) any later version.
+
+  systemd 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
+  Lesser General Public License for more details.
+
+  You should have received a copy of the GNU Lesser General Public License
+  along with util-linux; If not, see <http://www.gnu.org/licenses/>.
+***/
+
+#include <assert.h>
+#include <ctype.h>
+#include <string.h>
+#include <time.h>
+
+#include "c.h"
+#include "time-util.h"
+
+#define WHITESPACE " \t\n\r"
+
+#define streq(a,b) (strcmp((a),(b)) == 0)
+
+static char *startswith(const char *s, const char *prefix)
+{
+	const char *a, *b;
+
+	assert(s);
+	assert(prefix);
+
+	a = s, b = prefix;
+	for (;;) {
+		if (*b == 0)
+			return (char *)a;
+		if (*a != *b)
+			return NULL;
+
+		a++, b++;
+	}
+}
+
+static char *startswith_no_case(const char *s, const char *prefix)
+{
+	const char *a, *b;
+
+	assert(s);
+	assert(prefix);
+
+	a = s, b = prefix;
+	for (;;) {
+		if (*b == 0)
+			return (char *)a;
+		if (tolower(*a) != tolower(*b))
+			return NULL;
+
+		a++, b++;
+	}
+}
+
+static char *endswith(const char *s, const char *postfix)
+{
+	size_t sl, pl;
+
+	assert(s);
+	assert(postfix);
+
+	sl = strlen(s);
+	pl = strlen(postfix);
+
+	if (pl == 0)
+		return (char *)s + sl;
+
+	if (sl < pl)
+		return NULL;
+
+	if (memcmp(s + sl - pl, postfix, pl) != 0)
+		return NULL;
+
+	return (char *)s + sl - pl;
+}
+
+static int parse_sec(const char *t, usec_t *usec)
+{
+	 static const struct {
+		const char *suffix;
+		usec_t usec;
+	 } table[] = {
+		{ "seconds",	USEC_PER_SEC },
+		{ "second",	USEC_PER_SEC },
+		{ "sec",	USEC_PER_SEC },
+		{ "s",		USEC_PER_SEC },
+		{ "minutes",	USEC_PER_MINUTE },
+		{ "minute",	USEC_PER_MINUTE },
+		{ "min",	USEC_PER_MINUTE },
+		{ "months",	USEC_PER_MONTH },
+		{ "month",	USEC_PER_MONTH },
+		{ "msec",	USEC_PER_MSEC },
+		{ "ms",		USEC_PER_MSEC },
+		{ "m",		USEC_PER_MINUTE },
+		{ "hours",	USEC_PER_HOUR },
+		{ "hour",	USEC_PER_HOUR },
+		{ "hr",		USEC_PER_HOUR },
+		{ "h",		USEC_PER_HOUR },
+		{ "days",	USEC_PER_DAY },
+		{ "day",	USEC_PER_DAY },
+		{ "d",		USEC_PER_DAY },
+		{ "weeks",	USEC_PER_WEEK },
+		{ "week",	USEC_PER_WEEK },
+		{ "w",		USEC_PER_WEEK },
+		{ "years",	USEC_PER_YEAR },
+		{ "year",	USEC_PER_YEAR },
+		{ "y",		USEC_PER_YEAR },
+		{ "usec",	1ULL },
+		{ "us",		1ULL },
+		{ "",		USEC_PER_SEC },	/* default is sec */
+	 };
+
+	const char *p;
+	usec_t r = 0;
+	int something = FALSE;
+
+	assert(t);
+	assert(usec);
+
+	p = t;
+	for (;;) {
+		long long l, z = 0;
+		char *e;
+		unsigned i, n = 0;
+
+		p += strspn(p, WHITESPACE);
+
+		if (*p == 0) {
+			if (!something)
+				return -EINVAL;
+
+			break;
+		}
+
+		errno = 0;
+		l = strtoll(p, &e, 10);
+
+		if (errno > 0)
+			return -errno;
+
+		if (l < 0)
+			return -ERANGE;
+
+		if (*e == '.') {
+			char *b = e + 1;
+
+			errno = 0;
+			z = strtoll(b, &e, 10);
+			if (errno > 0)
+				return -errno;
+
+			if (z < 0)
+				return -ERANGE;
+
+			if (e == b)
+				return -EINVAL;
+
+			n = e - b;
+
+		} else if (e == p)
+			return -EINVAL;
+
+		e += strspn(e, WHITESPACE);
+
+		for (i = 0; i < ARRAY_SIZE(table); i++)
+			if (startswith(e, table[i].suffix)) {
+				usec_t k = (usec_t) z * table[i].usec;
+
+				for (; n > 0; n--)
+					k /= 10;
+
+				r += (usec_t) l *table[i].usec + k;
+				p = e + strlen(table[i].suffix);
+
+				something = TRUE;
+				break;
+			}
+
+		if (i >= ARRAY_SIZE(table))
+			return -EINVAL;
+
+	}
+
+	*usec = r;
+
+	return 0;
+}
+
+int parse_timestamp(const char *t, usec_t *usec)
+{
+	 static const struct {
+		const char *name;
+		const int nr;
+	 } day_nr[] = {
+		{ "Sunday",	0 },
+		{ "Sun",	0 },
+		{ "Monday",	1 },
+		{ "Mon",	1 },
+		{ "Tuesday",	2 },
+		{ "Tue",	2 },
+		{ "Wednesday",	3 },
+		{ "Wed",	3 },
+		{ "Thursday",	4 },
+		{ "Thu",	4 },
+		{ "Friday",	5 },
+		{ "Fri",	5 },
+		{ "Saturday",	6 },
+		{ "Sat",	6 },
+	 };
+
+	const char *k;
+	struct tm tm, copy;
+	time_t x;
+	usec_t plus = 0, minus = 0, ret;
+	int r, weekday = -1;
+	unsigned i;
+
+	/*
+	 * Allowed syntaxes:
+	 *
+	 *   2012-09-22 16:34:22
+	 *   2012-09-22 16:34	  (seconds will be set to 0)
+	 *   2012-09-22		  (time will be set to 00:00:00)
+	 *   16:34:22		  (date will be set to today)
+	 *   16:34		  (date will be set to today, seconds to 0)
+	 *   now
+	 *   yesterday		  (time is set to 00:00:00)
+	 *   today		  (time is set to 00:00:00)
+	 *   tomorrow		  (time is set to 00:00:00)
+	 *   +5min
+	 *   -5days
+	 *
+	 */
+
+	assert(t);
+	assert(usec);
+
+	x = time(NULL);
+	localtime_r(&x, &tm);
+	tm.tm_isdst = -1;
+
+	if (streq(t, "now"))
+		goto finish;
+
+	else if (streq(t, "today")) {
+		tm.tm_sec = tm.tm_min = tm.tm_hour = 0;
+		goto finish;
+
+	} else if (streq(t, "yesterday")) {
+		tm.tm_mday--;
+		tm.tm_sec = tm.tm_min = tm.tm_hour = 0;
+		goto finish;
+
+	} else if (streq(t, "tomorrow")) {
+		tm.tm_mday++;
+		tm.tm_sec = tm.tm_min = tm.tm_hour = 0;
+		goto finish;
+
+	} else if (t[0] == '+') {
+
+		r = parse_sec(t + 1, &plus);
+		if (r < 0)
+			return r;
+
+		goto finish;
+	} else if (t[0] == '-') {
+
+		r = parse_sec(t + 1, &minus);
+		if (r < 0)
+			return r;
+
+		goto finish;
+
+	} else if (endswith(t, " ago")) {
+		char *z;
+
+		z = strndup(t, strlen(t) - 4);
+		if (!z)
+			return -ENOMEM;
+
+		r = parse_sec(z, &minus);
+		if (r < 0)
+			return r;
+
+		goto finish;
+	}
+
+	for (i = 0; i < ARRAY_SIZE(day_nr); i++) {
+		size_t skip;
+
+		if (!startswith_no_case(t, day_nr[i].name))
+			continue;
+
+		skip = strlen(day_nr[i].name);
+		if (t[skip] != ' ')
+			continue;
+
+		weekday = day_nr[i].nr;
+		t += skip + 1;
+		break;
+	}
+
+	copy = tm;
+	k = strptime(t, "%y-%m-%d %H:%M:%S", &tm);
+	if (k && *k == 0)
+		goto finish;
+
+	tm = copy;
+	k = strptime(t, "%Y-%m-%d %H:%M:%S", &tm);
+	if (k && *k == 0)
+		goto finish;
+
+	tm = copy;
+	k = strptime(t, "%y-%m-%d %H:%M", &tm);
+	if (k && *k == 0) {
+		tm.tm_sec = 0;
+		goto finish;
+	}
+
+	tm = copy;
+	k = strptime(t, "%Y-%m-%d %H:%M", &tm);
+	if (k && *k == 0) {
+		tm.tm_sec = 0;
+		goto finish;
+	}
+
+	tm = copy;
+	k = strptime(t, "%y-%m-%d", &tm);
+	if (k && *k == 0) {
+		tm.tm_sec = tm.tm_min = tm.tm_hour = 0;
+		goto finish;
+	}
+
+	tm = copy;
+	k = strptime(t, "%Y-%m-%d", &tm);
+	if (k && *k == 0) {
+		tm.tm_sec = tm.tm_min = tm.tm_hour = 0;
+		goto finish;
+	}
+
+	tm = copy;
+	k = strptime(t, "%H:%M:%S", &tm);
+	if (k && *k == 0)
+		goto finish;
+
+	tm = copy;
+	k = strptime(t, "%H:%M", &tm);
+	if (k && *k == 0) {
+		tm.tm_sec = 0;
+		goto finish;
+	}
+
+	return -EINVAL;
+
+ finish:
+	x = mktime(&tm);
+	if (x == (time_t)-1)
+		return -EINVAL;
+
+	if (weekday >= 0 && tm.tm_wday != weekday)
+		return -EINVAL;
+
+	ret = (usec_t) x *USEC_PER_SEC;
+
+	ret += plus;
+	if (ret > minus)
+		ret -= minus;
+	else
+		ret = 0;
+
+	*usec = ret;
+
+	return 0;
+}
-- 
1.8.4


^ permalink raw reply related	[flat|nested] 25+ messages in thread

* [PATCH 11/17] last: parse easy to use time formats
  2013-08-27 18:06 [PATCH 00/17] pull: term-utils changes with emphasis on last(1) Sami Kerola
                   ` (8 preceding siblings ...)
  2013-08-27 18:06 ` [PATCH 10/17] lib/time-util: copy time parsing functions from systemd Sami Kerola
@ 2013-08-27 18:06 ` Sami Kerola
  2013-08-29 10:09   ` Karel Zak
  2013-08-27 18:06 ` [PATCH 12/17] last: add --since time spec option Sami Kerola
                   ` (6 subsequent siblings)
  16 siblings, 1 reply; 25+ messages in thread
From: Sami Kerola @ 2013-08-27 18:06 UTC (permalink / raw)
  To: util-linux; +Cc: kerolasa

Signed-off-by: Sami Kerola <kerolasa@iki.fi>
---
 login-utils/Makemodule.am |  1 +
 login-utils/last.c        | 12 ++++++++++--
 2 files changed, 11 insertions(+), 2 deletions(-)

diff --git a/login-utils/Makemodule.am b/login-utils/Makemodule.am
index 8d65cc5..d382ccc 100644
--- a/login-utils/Makemodule.am
+++ b/login-utils/Makemodule.am
@@ -5,6 +5,7 @@ dist_man_MANS += \
 	login-utils/last.1 \
 	login-utils/lastb.1
 last_SOURCES = login-utils/last.c lib/strutils.c
+last_LDADD = $(LDADD) libcommon.la
 
 install-exec-hook-last:
 	cd $(DESTDIR)$(usrsbin_execdir) && ln -sf last lastb
diff --git a/login-utils/last.c b/login-utils/last.c
index 3367038..df78436 100644
--- a/login-utils/last.c
+++ b/login-utils/last.c
@@ -47,6 +47,7 @@
 #include "closestream.h"
 #include "carefulputc.h"
 #include "strutils.h"
+#include "time-util.h"
 
 #ifndef SHUTDOWN_TIME
 # define SHUTDOWN_TIME 254
@@ -735,6 +736,7 @@ int main(int argc, char **argv)
 
 	time_t until = 0;	/* at what time to stop parsing the file */
 	time_t present = 0;	/* who where present at time_t */
+	usec_t p;
 
 	static const struct option long_opts[] = {
 	      { "limit",	required_argument, NULL, 'n' },
@@ -795,13 +797,19 @@ int main(int argc, char **argv)
 			break;
 		case 'p':
 			present = parsetm(optarg);
-			if (present == (time_t) -1)
+			if (present != (time_t) -1)
+				break;
+			if (parse_timestamp(optarg, &p) < 0)
 				errx(EXIT_FAILURE, _("invalid time value \"%s\""), optarg);
+			present = (time_t) (p / 1000000);
 			break;
 		case 't':
 			until = parsetm(optarg);
-			if (until == (time_t) -1)
+			if (until != (time_t) -1)
+				break;
+			if (parse_timestamp(optarg, &p) < 0)
 				errx(EXIT_FAILURE, _("invalid time value \"%s\""), optarg);
+			until = (time_t) (p / 1000000);
 			break;
 		case 'w':
 			if (UT_NAMESIZE > name_len)
-- 
1.8.4


^ permalink raw reply related	[flat|nested] 25+ messages in thread

* [PATCH 12/17] last: add --since time spec option
  2013-08-27 18:06 [PATCH 00/17] pull: term-utils changes with emphasis on last(1) Sami Kerola
                   ` (9 preceding siblings ...)
  2013-08-27 18:06 ` [PATCH 11/17] last: parse easy to use time formats Sami Kerola
@ 2013-08-27 18:06 ` Sami Kerola
  2013-08-27 18:06 ` [PATCH 13/17] docs: add --since and time option formats to last(1) manual Sami Kerola
                   ` (5 subsequent siblings)
  16 siblings, 0 replies; 25+ messages in thread
From: Sami Kerola @ 2013-08-27 18:06 UTC (permalink / raw)
  To: util-linux; +Cc: kerolasa

The --since and --until options are companions often needed together.

Signed-off-by: Sami Kerola <kerolasa@iki.fi>
---
 login-utils/last.c | 26 ++++++++++++++++++++------
 1 file changed, 20 insertions(+), 6 deletions(-)

diff --git a/login-utils/last.c b/login-utils/last.c
index df78436..c23469e 100644
--- a/login-utils/last.c
+++ b/login-utils/last.c
@@ -426,8 +426,9 @@ static void __attribute__((__noreturn__)) usage(FILE *out)
 	fputs(_(" -i, --ip             display IP numbers in numbers-and-dots notation\n"), out);
 	fputs(_(" -n, --limit <number> how many lines to show\n"), out);
 	fputs(_(" -R, --nohostname     don't display the hostname field\n"), out);
-	fputs(_(" -t, --until <YYYYMMDDHHMMSS>  display the state of the specified time\n"), out);
-	fputs(_(" -p, --present <YYYYMMDDHHMMSS>  display who where present at the specified time\n"), out);
+	fputs(_(" -s, --since <time>   display the lines since the specified time\n"), out);
+	fputs(_(" -t, --until <time>   display the lines until the specified time\n"), out);
+	fputs(_(" -p, --present <time> display who where present at the specified time\n"), out);
 	fputs(_(" -w, --fullnames      display full user and domain names\n"), out);
 	fputs(_(" -x, --system         display system shutdown entries and run level changes\n"), out);
 
@@ -480,7 +481,7 @@ static time_t parsetm(char *ts)
 }
 
 static void process_wtmp_file(char *ufile, int lastb, int extended,
-			      time_t until, time_t present)
+			      time_t since, time_t until, time_t present)
 {
 	FILE *fp;		/* Filepointer of wtmp file */
 
@@ -549,6 +550,9 @@ static void process_wtmp_file(char *ufile, int lastb, int extended,
 		if (uread(fp, &ut, &quit) != 1)
 			break;
 
+		if (since && ut.ut_time < since)
+			continue;
+
 		if (until && until < ut.ut_time)
 			continue;
 
@@ -734,7 +738,8 @@ int main(int argc, char **argv)
 	int lastb = 0;		/* Is this 'lastb' ? */
 	int extended = 0;	/* Lots of info. */
 
-	time_t until = 0;	/* at what time to stop parsing the file */
+	time_t since = 0;	/* at what time to start displaying the file */
+	time_t until = 0;	/* at what time to stop displaying the file */
 	time_t present = 0;	/* who where present at time_t */
 	usec_t p;
 
@@ -745,6 +750,7 @@ int main(int argc, char **argv)
 	      { "nohostname", no_argument,       NULL, 'R' },
 	      { "version",    no_argument,       NULL, 'V' },
 	      { "hostlast",   no_argument,       NULL, 'a' },
+	      { "since",      required_argument, NULL, 's' },
 	      { "until",      required_argument, NULL, 't' },
 	      { "present",    required_argument, NULL, 'p' },
 	      { "system",     no_argument,       NULL, 'x' },
@@ -761,7 +767,7 @@ int main(int argc, char **argv)
 	atexit(close_stdout);
 
 	while ((c = getopt_long(argc, argv,
-			"hVf:n:RxadFit:p:0123456789w", long_opts, NULL)) != -1) {
+			"hVf:n:RxadFit:p:s:0123456789w", long_opts, NULL)) != -1) {
 		switch(c) {
 		case 'h':
 			usage(stdout);
@@ -803,6 +809,14 @@ int main(int argc, char **argv)
 				errx(EXIT_FAILURE, _("invalid time value \"%s\""), optarg);
 			present = (time_t) (p / 1000000);
 			break;
+		case 's':
+			since = parsetm(optarg);
+			if (since != (time_t) -1)
+				break;
+			if (parse_timestamp(optarg, &p) < 0)
+				errx(EXIT_FAILURE, _("invalid time value \"%s\""), optarg);
+			since = (time_t) (p / 1000000);
+			break;
 		case 't':
 			until = parsetm(optarg);
 			if (until != (time_t) -1)
@@ -844,7 +858,7 @@ int main(int argc, char **argv)
 	}
 
 	for (i = 0; i < altc; i++) {
-		process_wtmp_file(altv[i], lastb, extended, until, present);
+		process_wtmp_file(altv[i], lastb, extended, since, until, present);
 		free(altv[i]);
 	}
 	free(altv);
-- 
1.8.4


^ permalink raw reply related	[flat|nested] 25+ messages in thread

* [PATCH 13/17] docs: add --since and time option formats to last(1) manual
  2013-08-27 18:06 [PATCH 00/17] pull: term-utils changes with emphasis on last(1) Sami Kerola
                   ` (10 preceding siblings ...)
  2013-08-27 18:06 ` [PATCH 12/17] last: add --since time spec option Sami Kerola
@ 2013-08-27 18:06 ` Sami Kerola
  2013-08-27 18:06 ` [PATCH 14/17] last: fix --present option logic error Sami Kerola
                   ` (4 subsequent siblings)
  16 siblings, 0 replies; 25+ messages in thread
From: Sami Kerola @ 2013-08-27 18:06 UTC (permalink / raw)
  To: util-linux; +Cc: kerolasa

Signed-off-by: Sami Kerola <kerolasa@iki.fi>
---
 login-utils/last.1 | 39 +++++++++++++++++++++++++++++++--------
 1 file changed, 31 insertions(+), 8 deletions(-)

diff --git a/login-utils/last.1 b/login-utils/last.1
index d348645..4c4dc02 100644
--- a/login-utils/last.1
+++ b/login-utils/last.1
@@ -88,17 +88,21 @@ This is a count telling
 .B last
 how many lines to show.
 .TP
-\fB\-t\fR, \fB\-\-until\fR \fIYYYYMMDDHHMMSS\fR
-Display the state of logins as of the specified time.  This is useful,
-e.g., to determine easily who was logged in at a particular time --
-specify that time with
-.B \-\-until
-and look for "still logged in".
+\fB\-s\fR, \fB\-\-since\fR \fItime\fR
+Display the state of logins since specified
+.IR time .
+This is useful, e.g., to determine easily who was logged in at a
+particular time.  The option is often combined with
+.BR \-\-until .
 .TP
-\fB\-p\fR, \fB\-\-present\fR \fIYYYYMMDDHHMMSS\fR
+\fB\-t\fR, \fB\-\-until\fR \fItime\fR
+Display the state of logins until the specified
+.IR time .
+.TP
+\fB\-p\fR, \fB\-\-present\fR \fItime\fR
 Display users who were present at the specified time.  This option is
 similar with
-.BR \-\-until ,
+.BR \-\-since " and " \-\-until ,
 without printing any other lines than the matching sessions.
 .TP
 \fB\-F\fR, \fB\-\-fulltimes\fR
@@ -131,6 +135,25 @@ Display full user and domain names in the output.
 .TP
 \fB\-x\fR, \fB\-\-system\fR
 Display the system shutdown entries and run level changes.
+.SH TIME FORMATS
+The options that take
+.I time
+argument will understand the following formats:
+.TS
+left l2 l2.
+YYYYMMDDhhmmss
+YYYY-MM-DD hh:mm:ss
+YYYY-MM-DD hh:mm	(seconds will be set to 00)
+YYYY-MM-DD	(time will be set to 00:00:00)
+hh:mm:ss	(date will be set to today)
+hh:mm	(date will be set to today, seconds to 00)
+now
+yesterday	(time is set to 00:00:00)
+today	(time is set to 00:00:00)
+tomorrow	(time is set to 00:00:00)
++5min
+-5days
+.TE
 .SH NOTES
 The files
 .I wtmp
-- 
1.8.4


^ permalink raw reply related	[flat|nested] 25+ messages in thread

* [PATCH 14/17] last: fix --present option logic error
  2013-08-27 18:06 [PATCH 00/17] pull: term-utils changes with emphasis on last(1) Sami Kerola
                   ` (11 preceding siblings ...)
  2013-08-27 18:06 ` [PATCH 13/17] docs: add --since and time option formats to last(1) manual Sami Kerola
@ 2013-08-27 18:06 ` Sami Kerola
  2013-08-27 18:06 ` [PATCH 15/17] last: use configuration struct Sami Kerola
                   ` (3 subsequent siblings)
  16 siblings, 0 replies; 25+ messages in thread
From: Sami Kerola @ 2013-08-27 18:06 UTC (permalink / raw)
  To: util-linux; +Cc: kerolasa

Printing of sessions 'still logged in' was skipped because an error in
logic.

Signed-off-by: Sami Kerola <kerolasa@iki.fi>
---
 login-utils/last.c | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/login-utils/last.c b/login-utils/last.c
index c23469e..7ab27c9 100644
--- a/login-utils/last.c
+++ b/login-utils/last.c
@@ -293,7 +293,7 @@ static int list(struct utmp *p, time_t t, int what, time_t present)
 	 */
 	tmp = (time_t)p->ut_time;
 
-	if (present && (present < tmp || 0 == t || t < present))
+	if (present && (present < tmp || (0 < t && t < present)))
 		return 0;
 
 	strcpy(logintime, ctime(&tmp));
-- 
1.8.4


^ permalink raw reply related	[flat|nested] 25+ messages in thread

* [PATCH 15/17] last: use configuration struct
  2013-08-27 18:06 [PATCH 00/17] pull: term-utils changes with emphasis on last(1) Sami Kerola
                   ` (12 preceding siblings ...)
  2013-08-27 18:06 ` [PATCH 14/17] last: fix --present option logic error Sami Kerola
@ 2013-08-27 18:06 ` Sami Kerola
  2013-08-29 10:15   ` Karel Zak
  2013-08-27 18:06 ` [PATCH 16/17] last: add --time-format with iso-8601 format Sami Kerola
                   ` (2 subsequent siblings)
  16 siblings, 1 reply; 25+ messages in thread
From: Sami Kerola @ 2013-08-27 18:06 UTC (permalink / raw)
  To: util-linux; +Cc: kerolasa

This allows reducing global variables and will minimize number of
arguments for functions making code a little bit easier to read, and
maintain.

Signed-off-by: Sami Kerola <kerolasa@iki.fi>
---
 login-utils/last.c | 221 +++++++++++++++++++++++++++++------------------------
 1 file changed, 120 insertions(+), 101 deletions(-)

diff --git a/login-utils/last.c b/login-utils/last.c
index 7ab27c9..2f01321 100644
--- a/login-utils/last.c
+++ b/login-utils/last.c
@@ -53,8 +53,40 @@
 # define SHUTDOWN_TIME 254
 #endif
 
+#ifndef LAST_LOGIN_LEN
+# define LAST_LOGIN_LEN 8
+#endif
+
+#ifndef LAST_DOMAIN_LEN
+# define LAST_DOMAIN_LEN 16
+#endif
+
 #define UCHUNKSIZE	16384	/* How much we read at once. */
 
+struct last_control {
+	char lastb;		/* Is this command 'lastb' */
+	char extended;		/* Lots of info */
+	char showhost;		/* Show hostname */
+	char altlist;		/* Hostname at the end */
+	char usedns;		/* Use DNS to lookup the hostname */
+	char useip;		/* Print IP address in number format */
+	char fulltime;		/* Print full dates and times */
+
+	unsigned int name_len;	/* Number of login name characters to print */
+	unsigned int domain_len; /* Number of domain name characters to print */
+	unsigned int maxrecs;	/* Maximum number of records to list */
+
+	char **show;		/* Match search list */
+
+	char **altv;		/* Alternate wtmp files */
+	unsigned int altc;	/* Number of alternative files */
+	unsigned int alti;	/* Index number of the alternative file */
+
+	time_t since;		/* at what time to start displaying the file */
+	time_t until;		/* at what time to stop displaying the file */
+	time_t present;		/* who where present at time_t */
+};
+
 /* Double linked list of struct utmp's */
 struct utmplist {
 	struct utmp ut;
@@ -75,24 +107,15 @@ enum {
 };
 
 /* Global variables */
-static unsigned int maxrecs;	/* Maximum number of records to list. */
 static unsigned int recsdone;	/* Number of records listed */
-static int showhost = 1;	/* Show hostname too? */
-static int altlist;		/* Show hostname at the end. */
-static int usedns;		/* Use DNS to lookup the hostname. */
-static int useip;		/* Print IP address in number format */
-static int fulltime;		/* Print full dates and times */
-static int name_len = 8;	/* Default print 8 characters of name */
-static int domain_len = 16;	/* Default print 16 characters of domain */
-static char **show = NULL;	/* What do they want us to show */
-static char *ufile;		/* Filename of this file */
 static time_t lastdate;		/* Last date we've seen */
 
 /*
  *	Read one utmp entry, return in new format.
  *	Automatically reposition file pointer.
  */
-static int uread(FILE *fp, struct utmp *u, int *quit)
+static int uread(const struct last_control *ctl, FILE *fp, struct utmp *u,
+		 int *quit)
 {
 	static int utsize;
 	static char buf[UCHUNKSIZE];
@@ -119,12 +142,12 @@ static int uread(FILE *fp, struct utmp *u, int *quit)
 			return 0;
 		o = ((fpos - 1) / UCHUNKSIZE) * UCHUNKSIZE;
 		if (fseeko(fp, o, SEEK_SET) < 0) {
-			warn(_("seek failed: %s"), ufile);
+			warn(_("seek failed: %s"), ctl->altv[ctl->alti]);
 			return 0;
 		}
 		bpos = (int)(fpos - o);
 		if (fread(buf, bpos, 1, fp) != 1) {
-			warn(_("read failed: %s"), ufile);
+			warn(_("read failed: %s"), ctl->altv[ctl->alti]);
 			return 0;
 		}
 		fpos = o;
@@ -153,7 +176,7 @@ static int uread(FILE *fp, struct utmp *u, int *quit)
 	 */
 	memcpy(tmp + (-bpos), buf, utsize + bpos);
 	if (fseeko(fp, fpos, SEEK_SET) < 0) {
-		warn(_("seek failed: %s"), ufile);
+		warn(_("seek failed: %s"), ctl->altv[ctl->alti]);
 		return 0;
 	}
 
@@ -161,7 +184,7 @@ static int uread(FILE *fp, struct utmp *u, int *quit)
 	 *	Read another UCHUNKSIZE bytes.
 	 */
 	if (fread(buf, UCHUNKSIZE, 1, fp) != 1) {
-		warn(_("read failed: %s"), ufile);
+		warn(_("read failed: %s"), ctl->altv[ctl->alti]);
 		return 0;
 	}
 
@@ -251,7 +274,7 @@ static int dns_lookup(char *result, int size, int useip, int32_t *a)
 /*
  *	Show one line of information on screen
  */
-static int list(struct utmp *p, time_t t, int what, time_t present)
+static int list(const struct last_control *ctl, struct utmp *p, time_t t, int what)
 {
 	time_t		secs, tmp;
 	char		logintime[32];
@@ -277,9 +300,9 @@ static int list(struct utmp *p, time_t t, int what, time_t present)
 	/*
 	 *	Is this something we wanna show?
 	 */
-	if (show) {
+	if (ctl->show) {
 		char **walk;
-		for (walk = show; *walk; walk++) {
+		for (walk = ctl->show; *walk; walk++) {
 			if (strncmp(p->ut_name, *walk, UT_NAMESIZE) == 0 ||
 			    strcmp(utline, *walk) == 0 ||
 			    (strncmp(utline, "tty", 3) == 0 &&
@@ -293,11 +316,11 @@ static int list(struct utmp *p, time_t t, int what, time_t present)
 	 */
 	tmp = (time_t)p->ut_time;
 
-	if (present && (present < tmp || (0 < t && t < present)))
+	if (ctl->present && (ctl->present < tmp || (0 < t && t < ctl->present)))
 		return 0;
 
 	strcpy(logintime, ctime(&tmp));
-	if (fulltime)
+	if (ctl->fulltime)
 		sprintf(logouttime, "- %s", ctime(&t));
 	else {
 		logintime[16] = 0;
@@ -322,7 +345,7 @@ static int list(struct utmp *p, time_t t, int what, time_t present)
 			break;
 		case R_NOW:
 			length[0] = 0;
-			if (fulltime)
+			if (ctl->fulltime)
 				sprintf(logouttime, "  still logged in");
 			else {
 				sprintf(logouttime, "  still");
@@ -331,7 +354,7 @@ static int list(struct utmp *p, time_t t, int what, time_t present)
 			break;
 		case R_PHANTOM:
 			length[0] = 0;
-			if (fulltime)
+			if (ctl->fulltime)
 				sprintf(logouttime, "  gone - no logout");
 			else {
 				sprintf(logouttime, "   gone");
@@ -354,8 +377,8 @@ static int list(struct utmp *p, time_t t, int what, time_t present)
 	 *	Look up host with DNS if needed.
 	 */
 	r = -1;
-	if (usedns || useip)
-		r = dns_lookup(domain, sizeof(domain), useip, p->ut_addr_v6);
+	if (ctl->usedns || ctl->useip)
+		r = dns_lookup(domain, sizeof(domain), ctl->useip, p->ut_addr_v6);
 	if (r < 0) {
 		len = UT_HOSTSIZE;
 		if (len >= (int)sizeof(domain)) len = sizeof(domain) - 1;
@@ -363,28 +386,28 @@ static int list(struct utmp *p, time_t t, int what, time_t present)
 		strncat(domain, p->ut_host, len);
 	}
 
-	if (showhost) {
-		if (!altlist) {
+	if (ctl->showhost) {
+		if (!ctl->altlist) {
 			len = snprintf(final, sizeof(final),
-				fulltime ?
+				ctl->fulltime ?
 				"%-8.*s %-12.12s %-16.*s %-24.24s %-26.26s %-12.12s\n" :
 				"%-8.*s %-12.12s %-16.*s %-16.16s %-7.7s %-12.12s\n",
-				name_len, p->ut_name, utline,
-				domain_len, domain, logintime, logouttime, length);
+				ctl->name_len, p->ut_name, utline,
+				ctl->domain_len, domain, logintime, logouttime, length);
 		} else {
 			len = snprintf(final, sizeof(final),
-				fulltime ?
+				ctl->fulltime ?
 				"%-8.*s %-12.12s %-24.24s %-26.26s %-12.12s %s\n" :
 				"%-8.*s %-12.12s %-16.16s %-7.7s %-12.12s %s\n",
-				name_len, p->ut_name, utline,
+				ctl->name_len, p->ut_name, utline,
 				logintime, logouttime, length, domain);
 		}
 	} else
 		len = snprintf(final, sizeof(final),
-			fulltime ?
+			ctl->fulltime ?
 			"%-8.*s %-12.12s %-24.24s %-26.26s %-12.12s\n" :
 			"%-8.*s %-12.12s %-16.16s %-7.7s %-12.12s\n",
-			name_len, p->ut_name, utline,
+			ctl->name_len, p->ut_name, utline,
 			logintime, logouttime, length);
 
 #if defined(__GLIBC__)
@@ -403,7 +426,7 @@ static int list(struct utmp *p, time_t t, int what, time_t present)
 		putchar('\n');
 
 	recsdone++;
-	if (maxrecs && recsdone >= maxrecs)
+	if (ctl->maxrecs && ctl->maxrecs <= recsdone)
 		return 1;
 
 	return 0;
@@ -480,8 +503,7 @@ static time_t parsetm(char *ts)
 	return tm;
 }
 
-static void process_wtmp_file(char *ufile, int lastb, int extended,
-			      time_t since, time_t until, time_t present)
+static void process_wtmp_file(const struct last_control *ctl)
 {
 	FILE *fp;		/* Filepointer of wtmp file */
 
@@ -517,8 +539,8 @@ static void process_wtmp_file(char *ufile, int lastb, int extended,
 	/*
 	 * Open the utmp file
 	 */
-	if ((fp = fopen(ufile, "r")) == NULL)
-		err(EXIT_FAILURE, _("cannot open %s"), ufile);
+	if ((fp = fopen(ctl->altv[ctl->alti], "r")) == NULL)
+		err(EXIT_FAILURE, _("cannot open %s"), ctl->altv[ctl->alti]);
 
 	/*
 	 * Optimize the buffer size.
@@ -528,7 +550,7 @@ static void process_wtmp_file(char *ufile, int lastb, int extended,
 	/*
 	 * Read first structure to capture the time field
 	 */
-	if (uread(fp, &ut, NULL) == 1)
+	if (uread(ctl, fp, &ut, NULL) == 1)
 		begintime = ut.ut_time;
 	else {
 		fstat(fileno(fp), &st);
@@ -540,26 +562,26 @@ static void process_wtmp_file(char *ufile, int lastb, int extended,
 	 * Go to end of file minus one structure
 	 * and/or initialize utmp reading code.
 	 */
-	uread(fp, NULL, NULL);
+	uread(ctl, fp, NULL, NULL);
 
 	/*
 	 * Read struct after struct backwards from the file.
 	 */
 	while (!quit) {
 
-		if (uread(fp, &ut, &quit) != 1)
+		if (uread(ctl, fp, &ut, &quit) != 1)
 			break;
 
-		if (since && ut.ut_time < since)
+		if (ctl->since && ut.ut_time < ctl->since)
 			continue;
 
-		if (until && until < ut.ut_time)
+		if (ctl->until && ctl->until < ut.ut_time)
 			continue;
 
 		lastdate = ut.ut_time;
 
-		if (lastb) {
-			quit = list(&ut, ut.ut_time, R_NORMAL, present);
+		if (ctl->lastb) {
+			quit = list(ctl, &ut, ut.ut_time, R_NORMAL);
 			continue;
 		}
 
@@ -605,33 +627,33 @@ static void process_wtmp_file(char *ufile, int lastb, int extended,
 #endif
 		switch (ut.ut_type) {
 		case SHUTDOWN_TIME:
-			if (extended) {
+			if (ctl->extended) {
 				strcpy(ut.ut_line, "system down");
-				quit = list(&ut, lastboot, R_NORMAL, present);
+				quit = list(ctl, &ut, lastboot, R_NORMAL);
 			}
 			lastdown = lastrch = ut.ut_time;
 			down = 1;
 			break;
 		case OLD_TIME:
 		case NEW_TIME:
-			if (extended) {
+			if (ctl->extended) {
 				strcpy(ut.ut_line,
 				ut.ut_type == NEW_TIME ? "new time" :
 					"old time");
-				quit = list(&ut, lastdown, R_TIMECHANGE, present);
+				quit = list(ctl, &ut, lastdown, R_TIMECHANGE);
 			}
 			break;
 		case BOOT_TIME:
 			strcpy(ut.ut_line, "system boot");
-			quit = list(&ut, lastdown, R_REBOOT, present);
+			quit = list(ctl, &ut, lastdown, R_REBOOT);
 			lastboot = ut.ut_time;
 			down = 1;
 			break;
 		case RUN_LVL:
 			x = ut.ut_pid & 255;
-			if (extended) {
+			if (ctl->extended) {
 				sprintf(ut.ut_line, "(to lvl %c)", x);
-				quit = list(&ut, lastrch, R_NORMAL, present);
+				quit = list(ctl, &ut, lastrch, R_NORMAL);
 			}
 			if (x == '0' || x == '6') {
 				lastdown = ut.ut_time;
@@ -654,8 +676,7 @@ static void process_wtmp_file(char *ufile, int lastb, int extended,
 				    UT_LINESIZE) == 0) {
 					/* Show it */
 					if (c == 0) {
-						quit = list(&ut, p->ut.ut_time,
-							R_NORMAL, present);
+						quit = list(ctl, &ut, p->ut.ut_time, R_NORMAL);
 						c = 1;
 					}
 					if (p->next) p->next->prev = p->prev;
@@ -680,7 +701,7 @@ static void process_wtmp_file(char *ufile, int lastb, int extended,
 						c = R_PHANTOM;
 				} else
 					c = whydown;
-				quit = list(&ut, lastboot, c, present);
+				quit = list(ctl, &ut, lastboot, c);
 			}
 			/* FALLTHRU */
 
@@ -726,21 +747,14 @@ static void process_wtmp_file(char *ufile, int lastb, int extended,
 		}
 	}
 
-	printf(_("\n%s begins %s"), basename(ufile), ctime(&begintime));
+	printf(_("\n%s begins %s"), basename(ctl->altv[ctl->alti]), ctime(&begintime));
 	fclose(fp);
 }
 
 int main(int argc, char **argv)
 {
-	int c, i;		/* Scratch */
-	char **altv = NULL;	/* Alternate wtmp files */
-	int altc = 0;		/* Number of alternative files */
-	int lastb = 0;		/* Is this 'lastb' ? */
-	int extended = 0;	/* Lots of info. */
-
-	time_t since = 0;	/* at what time to start displaying the file */
-	time_t until = 0;	/* at what time to stop displaying the file */
-	time_t present = 0;	/* who where present at time_t */
+	struct last_control ctl;
+	int c;
 	usec_t p;
 
 	static const struct option long_opts[] = {
@@ -761,6 +775,11 @@ int main(int argc, char **argv)
 	      { NULL, 0, NULL, 0 }
 	};
 
+	memset(&ctl, 0, sizeof(struct last_control));
+	ctl.showhost = TRUE;
+	ctl.name_len = LAST_LOGIN_LEN;
+	ctl.domain_len = LAST_DOMAIN_LEN;
+
 	setlocale(LC_ALL, "");
 	bindtextdomain(PACKAGE, LOCALEDIR);
 	textdomain(PACKAGE);
@@ -776,64 +795,64 @@ int main(int argc, char **argv)
 			printf(UTIL_LINUX_VERSION);
 			return EXIT_SUCCESS;
 		case 'R':
-			showhost = 0;
+			ctl.showhost = FALSE;
 			break;
 		case 'x':
-			extended = 1;
+			ctl.extended = TRUE;
 			break;
 		case 'n':
-			maxrecs = strtos32_or_err(optarg, _("failed to parse number"));
+			ctl.maxrecs = strtos32_or_err(optarg, _("failed to parse number"));
 			break;
 		case 'f':
-			if (!altv)
-				altv = xmalloc(sizeof(char *) * argc);
-			altv[altc++] = xstrdup(optarg);
+			if (!ctl.altv)
+				ctl.altv = xmalloc(sizeof(char *) * argc);
+			ctl.altv[ctl.altc++] = xstrdup(optarg);
 			break;
 		case 'd':
-			usedns++;
+			ctl.usedns = TRUE;
 			break;
 		case 'i':
-			useip++;
+			ctl.useip = TRUE;
 			break;
 		case 'a':
-			altlist++;
+			ctl.altlist = TRUE;
 			break;
 		case 'F':
-			fulltime++;
+			ctl.fulltime = TRUE;
 			break;
 		case 'p':
-			present = parsetm(optarg);
-			if (present != (time_t) -1)
+			ctl.present = parsetm(optarg);
+			if (ctl.present != (time_t) -1)
 				break;
 			if (parse_timestamp(optarg, &p) < 0)
 				errx(EXIT_FAILURE, _("invalid time value \"%s\""), optarg);
-			present = (time_t) (p / 1000000);
+			ctl.present = (time_t) (p / 1000000);
 			break;
 		case 's':
-			since = parsetm(optarg);
-			if (since != (time_t) -1)
+			ctl.since = parsetm(optarg);
+			if (ctl.since != (time_t) -1)
 				break;
 			if (parse_timestamp(optarg, &p) < 0)
 				errx(EXIT_FAILURE, _("invalid time value \"%s\""), optarg);
-			since = (time_t) (p / 1000000);
+			ctl.since = (time_t) (p / 1000000);
 			break;
 		case 't':
-			until = parsetm(optarg);
-			if (until != (time_t) -1)
+			ctl.until = parsetm(optarg);
+			if (ctl.until != (time_t) -1)
 				break;
 			if (parse_timestamp(optarg, &p) < 0)
 				errx(EXIT_FAILURE, _("invalid time value \"%s\""), optarg);
-			until = (time_t) (p / 1000000);
+			ctl.until = (time_t) (p / 1000000);
 			break;
 		case 'w':
-			if (UT_NAMESIZE > name_len)
-				name_len = UT_NAMESIZE;
-			if (UT_HOSTSIZE > domain_len)
-				domain_len = UT_HOSTSIZE;
+			if (ctl.name_len < UT_NAMESIZE)
+				ctl.name_len = UT_NAMESIZE;
+			if (ctl.domain_len < UT_HOSTSIZE)
+				ctl.domain_len = UT_HOSTSIZE;
 			break;
 		case '0': case '1': case '2': case '3': case '4':
 		case '5': case '6': case '7': case '8': case '9':
-			maxrecs = 10 * maxrecs + c - '0';
+			ctl.maxrecs = 10 * ctl.maxrecs + c - '0';
 			break;
 		default:
 			usage(stderr);
@@ -842,25 +861,25 @@ int main(int argc, char **argv)
 	}
 
 	if (optind < argc)
-		show = argv + optind;
+		ctl.show = argv + optind;
 
 	/*
 	 * Which file do we want to read?
 	 */
-	lastb = !strcmp(program_invocation_short_name, "lastb");
-	if (!altc) {
-		altv = xmalloc(sizeof(char *));
-		if (lastb)
-			altv[0] = xstrdup(_PATH_BTMP);
+	ctl.lastb = !strcmp(program_invocation_short_name, "lastb");
+	if (!ctl.altc) {
+		ctl.altv = xmalloc(sizeof(char *));
+		if (ctl.lastb)
+			ctl.altv[0] = xstrdup(_PATH_BTMP);
 		else
-			altv[0] = xstrdup(_PATH_WTMP);
-		altc++;
+			ctl.altv[0] = xstrdup(_PATH_WTMP);
+		ctl.altc++;
 	}
 
-	for (i = 0; i < altc; i++) {
-		process_wtmp_file(altv[i], lastb, extended, since, until, present);
-		free(altv[i]);
+	for (; ctl.alti < ctl.altc; ctl.alti++) {
+		process_wtmp_file(&ctl);
+		free(ctl.altv[ctl.alti]);
 	}
-	free(altv);
+	free(ctl.altv);
 	return EXIT_SUCCESS;
 }
-- 
1.8.4


^ permalink raw reply related	[flat|nested] 25+ messages in thread

* [PATCH 16/17] last: add --time-format with iso-8601 format
  2013-08-27 18:06 [PATCH 00/17] pull: term-utils changes with emphasis on last(1) Sami Kerola
                   ` (13 preceding siblings ...)
  2013-08-27 18:06 ` [PATCH 15/17] last: use configuration struct Sami Kerola
@ 2013-08-27 18:06 ` Sami Kerola
  2013-08-27 18:06 ` [PATCH 17/17] docs: add --time-format to last(1) manual page Sami Kerola
  2013-09-02  9:59 ` [PATCH 00/17] pull: term-utils changes with emphasis on last(1) Karel Zak
  16 siblings, 0 replies; 25+ messages in thread
From: Sami Kerola @ 2013-08-27 18:06 UTC (permalink / raw)
  To: util-linux; +Cc: kerolasa

The ISO-8601 format makes consuming time stamps easy with various
parsers.  The format includes time zone information which is crucial when
an investigator is trying to make sense outputs collected from systems
all a across planet.

Signed-off-by: Sami Kerola <kerolasa@iki.fi>
---
 login-utils/last.c | 134 ++++++++++++++++++++++++++++++++++++++++++++---------
 1 file changed, 111 insertions(+), 23 deletions(-)

diff --git a/login-utils/last.c b/login-utils/last.c
index 2f01321..4733c64 100644
--- a/login-utils/last.c
+++ b/login-utils/last.c
@@ -61,6 +61,10 @@
 # define LAST_DOMAIN_LEN 16
 #endif
 
+#ifndef LAST_TIMESTAMP_LEN
+# define LAST_TIMESTAMP_LEN 32
+#endif
+
 #define UCHUNKSIZE	16384	/* How much we read at once. */
 
 struct last_control {
@@ -85,6 +89,7 @@ struct last_control {
 	time_t since;		/* at what time to start displaying the file */
 	time_t until;		/* at what time to stop displaying the file */
 	time_t present;		/* who where present at time_t */
+	unsigned int time_fmt;	/* time format */
 };
 
 /* Double linked list of struct utmp's */
@@ -106,10 +111,43 @@ enum {
 	R_TIMECHANGE	/* NEW_TIME or OLD_TIME */
 };
 
+enum {
+	LAST_TIMEFTM_NONE = 0,
+	LAST_TIMEFTM_SHORT_CTIME,
+	LAST_TIMEFTM_FULL_CTIME,
+	LAST_TIMEFTM_ISO8601
+};
+
+struct last_timefmt_lens {
+	int in;
+	int out;
+};
+
+static struct last_timefmt_lens tftl[] = {
+	[LAST_TIMEFTM_NONE] 	   = {0, 0},
+	[LAST_TIMEFTM_SHORT_CTIME] = {16, 7},
+	[LAST_TIMEFTM_FULL_CTIME]  = {24, 26},
+	[LAST_TIMEFTM_ISO8601]	   = {24, 26}
+};
+
 /* Global variables */
 static unsigned int recsdone;	/* Number of records listed */
 static time_t lastdate;		/* Last date we've seen */
 
+/* --time-format=option parser */
+static int which_time_format(const char *optarg)
+{
+	if (!strcmp(optarg, "notime"))
+		return LAST_TIMEFTM_NONE;
+	if (!strcmp(optarg, "short"))
+		return LAST_TIMEFTM_SHORT_CTIME;
+	if (!strcmp(optarg, "full"))
+		return LAST_TIMEFTM_FULL_CTIME;
+	if (!strcmp(optarg, "iso"))
+		return LAST_TIMEFTM_ISO8601;
+	errx(EXIT_FAILURE, _("unknown time format: %s"), optarg);
+}
+
 /*
  *	Read one utmp entry, return in new format.
  *	Automatically reposition file pointer.
@@ -271,15 +309,54 @@ static int dns_lookup(char *result, int size, int useip, int32_t *a)
 	return getnameinfo(sa, salen, result, size, NULL, 0, flags);
 }
 
+static int time_formatter(const struct last_control *ctl, char *dst,
+			  size_t dlen, time_t *when, int pos)
+{
+	struct tm *tm;
+	int ret = 0;
+
+	switch (ctl->time_fmt) {
+	case LAST_TIMEFTM_NONE:
+		*dst = 0;
+		break;
+	case LAST_TIMEFTM_SHORT_CTIME:
+		if (pos == 0)
+			ret = sprintf(dst, "%s", ctime(when));
+		else {
+			tm = localtime(when);
+			if (!strftime(dst, dlen, "- %H:%M", tm))
+				ret = -1;
+		}
+		break;
+	case LAST_TIMEFTM_FULL_CTIME:
+		if (pos == 0)
+			ret = sprintf(dst, "%s", ctime(when));
+		else
+			ret = sprintf(dst, "- %s", ctime(when));
+		break;
+	case LAST_TIMEFTM_ISO8601:
+		tm = localtime(when);
+		if (pos == 0) {
+			if (!strftime(dst, dlen, "%Y-%m-%dT%H:%M:%S%z", tm))
+				ret = -1;
+		} else if (!strftime(dst, dlen, "- %Y-%m-%dT%H:%M:%S%z", tm))
+			ret = -1;
+		break;
+	default:
+		abort();
+	}
+	return ret;
+}
+
 /*
  *	Show one line of information on screen
  */
 static int list(const struct last_control *ctl, struct utmp *p, time_t t, int what)
 {
 	time_t		secs, tmp;
-	char		logintime[32];
-	char		logouttime[32];
-	char		length[32];
+	char		logintime[LAST_TIMESTAMP_LEN];
+	char		logouttime[LAST_TIMESTAMP_LEN];
+	char		length[LAST_TIMESTAMP_LEN];
 	char		final[512];
 	char		utline[UT_LINESIZE+1];
 	char		domain[256];
@@ -319,14 +396,10 @@ static int list(const struct last_control *ctl, struct utmp *p, time_t t, int wh
 	if (ctl->present && (ctl->present < tmp || (0 < t && t < ctl->present)))
 		return 0;
 
-	strcpy(logintime, ctime(&tmp));
-	if (ctl->fulltime)
-		sprintf(logouttime, "- %s", ctime(&t));
-	else {
-		logintime[16] = 0;
-		sprintf(logouttime, "- %s", ctime(&t) + 11);
-		logouttime[7] = 0;
-	}
+	if (time_formatter(ctl, &logintime[0], sizeof(logintime), &tmp, 0) < 0 ||
+	    time_formatter(ctl, &logouttime[0], sizeof(logouttime), &t, 1) < 0)
+		errx(EXIT_FAILURE, _("preallocation size exceeded"));
+
 	secs = t - p->ut_time;
 	mins  = (secs / 60) % 60;
 	hours = (secs / 3600) % 24;
@@ -389,26 +462,27 @@ static int list(const struct last_control *ctl, struct utmp *p, time_t t, int wh
 	if (ctl->showhost) {
 		if (!ctl->altlist) {
 			len = snprintf(final, sizeof(final),
-				ctl->fulltime ?
-				"%-8.*s %-12.12s %-16.*s %-24.24s %-26.26s %-12.12s\n" :
-				"%-8.*s %-12.12s %-16.*s %-16.16s %-7.7s %-12.12s\n",
+				"%-8.*s %-12.12s %-16.*s %-*.*s %-*.*s %-12.12s\n",
 				ctl->name_len, p->ut_name, utline,
-				ctl->domain_len, domain, logintime, logouttime, length);
+				ctl->domain_len, domain,
+				tftl[ctl->time_fmt].in, tftl[ctl->time_fmt].in, logintime,
+				tftl[ctl->time_fmt].out, tftl[ctl->time_fmt].out, logouttime,
+				length);
 		} else {
 			len = snprintf(final, sizeof(final),
-				ctl->fulltime ?
-				"%-8.*s %-12.12s %-24.24s %-26.26s %-12.12s %s\n" :
-				"%-8.*s %-12.12s %-16.16s %-7.7s %-12.12s %s\n",
+				"%-8.*s %-12.12s %-*.*s %-*.*s %-12.12s %s\n",
 				ctl->name_len, p->ut_name, utline,
-				logintime, logouttime, length, domain);
+				tftl[ctl->time_fmt].in, tftl[ctl->time_fmt].in, logintime,
+				tftl[ctl->time_fmt].out, tftl[ctl->time_fmt].out, logouttime,
+				length, domain);
 		}
 	} else
 		len = snprintf(final, sizeof(final),
-			ctl->fulltime ?
-			"%-8.*s %-12.12s %-24.24s %-26.26s %-12.12s\n" :
-			"%-8.*s %-12.12s %-16.16s %-7.7s %-12.12s\n",
+			"%-8.*s %-12.12s %-*.*s %-*.*s %-12.12s\n",
 			ctl->name_len, p->ut_name, utline,
-			logintime, logouttime, length);
+			tftl[ctl->time_fmt].in, tftl[ctl->time_fmt].in, logintime,
+			tftl[ctl->time_fmt].out, tftl[ctl->time_fmt].out, logouttime,
+			length);
 
 #if defined(__GLIBC__)
 #  if (__GLIBC__ == 2) && (__GLIBC_MINOR__ == 0)
@@ -454,6 +528,8 @@ static void __attribute__((__noreturn__)) usage(FILE *out)
 	fputs(_(" -p, --present <time> display who where present at the specified time\n"), out);
 	fputs(_(" -w, --fullnames      display full user and domain names\n"), out);
 	fputs(_(" -x, --system         display system shutdown entries and run level changes\n"), out);
+	fputs(_("     --time-format <format>  show time stamp using format:\n"), out);
+	fputs(_("                               [notime|short|full|iso]\n"), out);
 
 	fputs(USAGE_SEPARATOR, out);
 	fputs(USAGE_HELP, out);
@@ -757,6 +833,10 @@ int main(int argc, char **argv)
 	int c;
 	usec_t p;
 
+	enum {
+		OPT_TIME_FORMAT = CHAR_MAX + 1
+	};
+
 	static const struct option long_opts[] = {
 	      { "limit",	required_argument, NULL, 'n' },
 	      { "help",	no_argument,       NULL, 'h' },
@@ -772,6 +852,7 @@ int main(int argc, char **argv)
 	      { "ip",         no_argument,       NULL, 'i' },
 	      { "fulltimes",  no_argument,       NULL, 'F' },
 	      { "fullnames",  no_argument,       NULL, 'w' },
+	      { "time-format", required_argument, NULL, OPT_TIME_FORMAT },
 	      { NULL, 0, NULL, 0 }
 	};
 
@@ -779,6 +860,7 @@ int main(int argc, char **argv)
 	ctl.showhost = TRUE;
 	ctl.name_len = LAST_LOGIN_LEN;
 	ctl.domain_len = LAST_DOMAIN_LEN;
+	ctl.time_fmt = LAST_TIMEFTM_SHORT_CTIME;
 
 	setlocale(LC_ALL, "");
 	bindtextdomain(PACKAGE, LOCALEDIR);
@@ -819,6 +901,7 @@ int main(int argc, char **argv)
 			break;
 		case 'F':
 			ctl.fulltime = TRUE;
+			ctl.time_fmt = LAST_TIMEFTM_FULL_CTIME;
 			break;
 		case 'p':
 			ctl.present = parsetm(optarg);
@@ -854,6 +937,11 @@ int main(int argc, char **argv)
 		case '5': case '6': case '7': case '8': case '9':
 			ctl.maxrecs = 10 * ctl.maxrecs + c - '0';
 			break;
+		case OPT_TIME_FORMAT:
+			ctl.time_fmt = which_time_format(optarg);
+			if (ctl.time_fmt == LAST_TIMEFTM_ISO8601)
+				ctl.fulltime = TRUE;
+			break;
 		default:
 			usage(stderr);
 			break;
-- 
1.8.4


^ permalink raw reply related	[flat|nested] 25+ messages in thread

* [PATCH 17/17] docs: add --time-format to last(1) manual page
  2013-08-27 18:06 [PATCH 00/17] pull: term-utils changes with emphasis on last(1) Sami Kerola
                   ` (14 preceding siblings ...)
  2013-08-27 18:06 ` [PATCH 16/17] last: add --time-format with iso-8601 format Sami Kerola
@ 2013-08-27 18:06 ` Sami Kerola
  2013-09-02  9:59 ` [PATCH 00/17] pull: term-utils changes with emphasis on last(1) Karel Zak
  16 siblings, 0 replies; 25+ messages in thread
From: Sami Kerola @ 2013-08-27 18:06 UTC (permalink / raw)
  To: util-linux; +Cc: kerolasa

Signed-off-by: Sami Kerola <kerolasa@iki.fi>
---
 login-utils/last.1 | 22 ++++++++++++++++++++++
 1 file changed, 22 insertions(+)

diff --git a/login-utils/last.1 b/login-utils/last.1
index 4c4dc02..13b4693 100644
--- a/login-utils/last.1
+++ b/login-utils/last.1
@@ -105,6 +105,28 @@ similar with
 .BR \-\-since " and " \-\-until ,
 without printing any other lines than the matching sessions.
 .TP
+\fB\-\-time\-format\fR \fIformat\fR
+Define output timestamp
+.I format
+to be one of the
+.IR notime ,
+.IR short ,
+.IR full ,
+or
+.IR iso .
+.I notime
+will not print timestamp at all.
+.I short
+is default, and
+.I full
+is same as
+.B \-\-fulltimes
+option.  The
+.I iso
+will display time stamp in ISO-8601 format.  The iso format contains
+timezone information making it to be preferrable when print outs are
+investigated outside of the system.
+.TP
 \fB\-F\fR, \fB\-\-fulltimes\fR
 Print full login and logout times and dates.
 .TP
-- 
1.8.4


^ permalink raw reply related	[flat|nested] 25+ messages in thread

* Re: [PATCH 11/17] last: parse easy to use time formats
  2013-08-27 18:06 ` [PATCH 11/17] last: parse easy to use time formats Sami Kerola
@ 2013-08-29 10:09   ` Karel Zak
  2013-08-29 18:01     ` Sami Kerola
  0 siblings, 1 reply; 25+ messages in thread
From: Karel Zak @ 2013-08-29 10:09 UTC (permalink / raw)
  To: Sami Kerola; +Cc: util-linux

On Tue, Aug 27, 2013 at 07:06:13PM +0100, Sami Kerola wrote:
>  		case 'p':
>  			present = parsetm(optarg);
> -			if (present == (time_t) -1)
> +			if (present != (time_t) -1)
> +				break;
> +			if (parse_timestamp(optarg, &p) < 0)
>  				errx(EXIT_FAILURE, _("invalid time value \"%s\""), optarg);
> +			present = (time_t) (p / 1000000);
>  			break;

 It would be better to add support for YYYYMMDDHHMMSS into
 parse_timestamp() rather than maintain and use two parsers.

    Karel

-- 
 Karel Zak  <kzak@redhat.com>
 http://karelzak.blogspot.com

^ permalink raw reply	[flat|nested] 25+ messages in thread

* Re: [PATCH 10/17] lib/time-util: copy time parsing functions from systemd
  2013-08-27 18:06 ` [PATCH 10/17] lib/time-util: copy time parsing functions from systemd Sami Kerola
@ 2013-08-29 10:11   ` Karel Zak
  2013-08-29 18:02     ` Sami Kerola
  0 siblings, 1 reply; 25+ messages in thread
From: Karel Zak @ 2013-08-29 10:11 UTC (permalink / raw)
  To: Sami Kerola; +Cc: util-linux

On Tue, Aug 27, 2013 at 07:06:12PM +0100, Sami Kerola wrote:
> +static char *startswith(const char *s, const char *prefix)
> +static char *startswith_no_case(const char *s, const char *prefix)
> +static char *endswith(const char *s, const char *postfix)

 We already have startswith() and endswith() in libmount/src/utils.c.

 What about to move the code to lib/strutils.c (together with
 *_no_case() versions)?

    Karel

-- 
 Karel Zak  <kzak@redhat.com>
 http://karelzak.blogspot.com

^ permalink raw reply	[flat|nested] 25+ messages in thread

* Re: [PATCH 15/17] last: use configuration struct
  2013-08-27 18:06 ` [PATCH 15/17] last: use configuration struct Sami Kerola
@ 2013-08-29 10:15   ` Karel Zak
  2013-08-29 18:06     ` Sami Kerola
  0 siblings, 1 reply; 25+ messages in thread
From: Karel Zak @ 2013-08-29 10:15 UTC (permalink / raw)
  To: Sami Kerola; +Cc: util-linux

On Tue, Aug 27, 2013 at 07:06:17PM +0100, Sami Kerola wrote:
> +	struct last_control ctl;
> +	int c;
>  	usec_t p;
>  
>  	static const struct option long_opts[] = {
> @@ -761,6 +775,11 @@ int main(int argc, char **argv)
>  	      { NULL, 0, NULL, 0 }
>  	};
>  
> +	memset(&ctl, 0, sizeof(struct last_control));
> +	ctl.showhost = TRUE;
> +	ctl.name_len = LAST_LOGIN_LEN;
> +	ctl.domain_len = LAST_DOMAIN_LEN;
> +

  struct last_control ctl = {
         .showhost = TRUE,
         .name_len = LAST_LOGIN_LEN,
         .domain_len = LAST_DOMAIN_LEN
  };

 is good enough, memset() is unnecessary.


    Karel

-- 
 Karel Zak  <kzak@redhat.com>
 http://karelzak.blogspot.com

^ permalink raw reply	[flat|nested] 25+ messages in thread

* Re: [PATCH 11/17] last: parse easy to use time formats
  2013-08-29 10:09   ` Karel Zak
@ 2013-08-29 18:01     ` Sami Kerola
  0 siblings, 0 replies; 25+ messages in thread
From: Sami Kerola @ 2013-08-29 18:01 UTC (permalink / raw)
  To: Karel Zak; +Cc: util-linux

On Thu, Aug 29, 2013 at 12:09:19PM +0200, Karel Zak wrote:
> On Tue, Aug 27, 2013 at 07:06:13PM +0100, Sami Kerola wrote:
> >  		case 'p':
> >  			present = parsetm(optarg);
> > -			if (present == (time_t) -1)
> > +			if (present != (time_t) -1)
> > +				break;
> > +			if (parse_timestamp(optarg, &p) < 0)
> >  				errx(EXIT_FAILURE, _("invalid time value \"%s\""), optarg);
> > +			present = (time_t) (p / 1000000);
> >  			break;
> 
>  It would be better to add support for YYYYMMDDHHMMSS into
>  parse_timestamp() rather than maintain and use two parsers.

Done as proposed.

This, and there rest of the changes I will sent shortly, are also in the
git repository at:

  git://github.com/kerolasa/lelux-utiliteetit.git 2013wk34



>From 10e295c115849c0467eee550fb5b2176e6885839 Mon Sep 17 00:00:00 2001
From: Sami Kerola <kerolasa@iki.fi>
Date: Thu, 29 Aug 2013 16:11:41 +0100
Subject: [PATCH 13/26] lib/time-util: move YYYYMMDDHHMMSS to common parser
Organization: Lastminute.com

Even while the YYYYMMDDHHMMSS time format it not magnificent it is best
to make it to be part of the one, and only, time format parser.

Proposed-by: Karel Zak <kzak@redhat.com>
References: http://markmail.org/message/6baqt4ttkopu7ra6
Signed-off-by: Sami Kerola <kerolasa@iki.fi>
---
 lib/time-util.c    |  7 +++++++
 login-utils/last.c | 45 ---------------------------------------------
 2 files changed, 7 insertions(+), 45 deletions(-)

diff --git a/lib/time-util.c b/lib/time-util.c
index a0b27bf..e027bac 100644
--- a/lib/time-util.c
+++ b/lib/time-util.c
@@ -306,6 +306,13 @@ int parse_timestamp(const char *t, usec_t *usec)
 		goto finish;
 	}
 
+	tm = copy;
+	k = strptime(t, "%Y%m%d%H%M%S", &tm);
+	if (k && *k == 0) {
+		tm.tm_sec = 0;
+		goto finish;
+	}
+
 	return -EINVAL;
 
  finish:
diff --git a/login-utils/last.c b/login-utils/last.c
index df78436..410e612 100644
--- a/login-utils/last.c
+++ b/login-utils/last.c
@@ -440,45 +440,6 @@ static void __attribute__((__noreturn__)) usage(FILE *out)
 }
 
 
-static time_t parsetm(char *ts)
-{
-	struct tm	u, origu;
-	time_t		tm;
-
-	memset(&tm, 0, sizeof(tm));
-
-	if (sscanf(ts, "%4d%2d%2d%2d%2d%2d", &u.tm_year,
-	    &u.tm_mon, &u.tm_mday, &u.tm_hour, &u.tm_min,
-	    &u.tm_sec) != 6)
-		return (time_t)-1;
-
-	u.tm_year -= 1900;
-	u.tm_mon -= 1;
-	u.tm_isdst = -1;
-
-	origu = u;
-
-	if ((tm = mktime(&u)) == (time_t)-1)
-		return tm;
-
-	/*
-	 *	Unfortunately mktime() is much more forgiving than
-	 *	it should be.  For example, it'll gladly accept
-	 *	"30" as a valid month number.  This behavior is by
-	 *	design, but we don't like it, so we want to detect
-	 *	it and complain.
-	 */
-	if (u.tm_year != origu.tm_year ||
-	    u.tm_mon != origu.tm_mon ||
-	    u.tm_mday != origu.tm_mday ||
-	    u.tm_hour != origu.tm_hour ||
-	    u.tm_min != origu.tm_min ||
-	    u.tm_sec != origu.tm_sec)
-		return (time_t)-1;
-
-	return tm;
-}
-
 static void process_wtmp_file(char *ufile, int lastb, int extended,
 			      time_t until, time_t present)
 {
@@ -796,17 +757,11 @@ int main(int argc, char **argv)
 			fulltime++;
 			break;
 		case 'p':
-			present = parsetm(optarg);
-			if (present != (time_t) -1)
-				break;
 			if (parse_timestamp(optarg, &p) < 0)
 				errx(EXIT_FAILURE, _("invalid time value \"%s\""), optarg);
 			present = (time_t) (p / 1000000);
 			break;
 		case 't':
-			until = parsetm(optarg);
-			if (until != (time_t) -1)
-				break;
 			if (parse_timestamp(optarg, &p) < 0)
 				errx(EXIT_FAILURE, _("invalid time value \"%s\""), optarg);
 			until = (time_t) (p / 1000000);
-- 
1.8.4




-- 
   Sami Kerola
   http://www.iki.fi/kerolasa/

^ permalink raw reply related	[flat|nested] 25+ messages in thread

* Re: [PATCH 10/17] lib/time-util: copy time parsing functions from systemd
  2013-08-29 10:11   ` Karel Zak
@ 2013-08-29 18:02     ` Sami Kerola
  0 siblings, 0 replies; 25+ messages in thread
From: Sami Kerola @ 2013-08-29 18:02 UTC (permalink / raw)
  To: Karel Zak; +Cc: util-linux

On Thu, Aug 29, 2013 at 12:11:27PM +0200, Karel Zak wrote:
> On Tue, Aug 27, 2013 at 07:06:12PM +0100, Sami Kerola wrote:
> > +static char *startswith(const char *s, const char *prefix)
> > +static char *startswith_no_case(const char *s, const char *prefix)
> > +static char *endswith(const char *s, const char *postfix)
> 
>  We already have startswith() and endswith() in libmount/src/utils.c.
> 
>  What about to move the code to lib/strutils.c (together with
>  *_no_case() versions)?

I hope the following is roughly what you where thinking.



>From a1969bc9f9d08b05c2fa6f750388cfdf6033fc1a Mon Sep 17 00:00:00 2001
From: Sami Kerola <kerolasa@iki.fi>
Date: Thu, 29 Aug 2013 15:50:17 +0100
Subject: [PATCH 11/26] lib/strutils: move *swith() functions to private
 library
Organization: Lastminute.com

Avoid code dublication in libmount and time-util.

Proposed-by: Karel Zak <kzak@redhat.com>
Reference: http://markmail.org/message/h7zexvqsieqngtmx
Signed-off-by: Sami Kerola <kerolasa@iki.fi>
---
 include/strutils.h           |  4 +++
 lib/strutils.c               | 67 ++++++++++++++++++++++++++++++++++++++++++++
 lib/time-util.c              | 58 --------------------------------------
 libmount/src/context_mount.c |  1 +
 libmount/src/mountP.h        |  5 ----
 libmount/src/optmap.c        |  1 +
 libmount/src/utils.c         | 31 --------------------
 7 files changed, 73 insertions(+), 94 deletions(-)

diff --git a/include/strutils.h b/include/strutils.h
index 709fcad..8d8a6e4 100644
--- a/include/strutils.h
+++ b/include/strutils.h
@@ -102,4 +102,8 @@ extern int parse_range(const char *str, int *lower, int *upper, int def);
 
 extern int streq_except_trailing_slash(const char *s1, const char *s2);
 
+extern char *startswith(const char *s, const char *prefix);
+extern char *startswith_no_case(const char *s, const char *prefix);
+extern char *endswith(const char *s, const char *postfix);
+
 #endif
diff --git a/lib/strutils.c b/lib/strutils.c
index c263b86..95ab5a8 100644
--- a/lib/strutils.c
+++ b/lib/strutils.c
@@ -10,6 +10,7 @@
 #include <errno.h>
 #include <sys/stat.h>
 #include <string.h>
+#include <assert.h>
 
 #include "c.h"
 #include "nls.h"
@@ -685,6 +686,72 @@ int streq_except_trailing_slash(const char *s1, const char *s2)
 	return equal;
 }
 
+/*
+ * Match string beginning.
+ */
+char *startswith(const char *s, const char *prefix)
+{
+	const char *a, *b;
+
+	assert(s);
+	assert(prefix);
+
+	a = s, b = prefix;
+	for (;;) {
+		if (*b == 0)
+			return (char *)a;
+		if (*a != *b)
+			return NULL;
+
+		a++, b++;
+	}
+}
+
+/*
+ * Case insensitive match string beginning.
+ */
+char *startswith_no_case(const char *s, const char *prefix)
+{
+	const char *a, *b;
+
+	assert(s);
+	assert(prefix);
+
+	a = s, b = prefix;
+	for (;;) {
+		if (*b == 0)
+			return (char *)a;
+		if (tolower(*a) != tolower(*b))
+			return NULL;
+
+		a++, b++;
+	}
+}
+
+/*
+ * Match string ending.
+ */
+char *endswith(const char *s, const char *postfix)
+{
+	size_t sl, pl;
+
+	assert(s);
+	assert(postfix);
+
+	sl = strlen(s);
+	pl = strlen(postfix);
+
+	if (pl == 0)
+		return (char *)s + sl;
+
+	if (sl < pl)
+		return NULL;
+
+	if (memcmp(s + sl - pl, postfix, pl) != 0)
+		return NULL;
+
+	return (char *)s + sl - pl;
+}
 
 #ifdef TEST_PROGRAM
 
diff --git a/lib/time-util.c b/lib/time-util.c
index c8fcc2a..a0b27bf 100644
--- a/lib/time-util.c
+++ b/lib/time-util.c
@@ -30,64 +30,6 @@
 
 #define streq(a,b) (strcmp((a),(b)) == 0)
 
-static char *startswith(const char *s, const char *prefix)
-{
-	const char *a, *b;
-
-	assert(s);
-	assert(prefix);
-
-	a = s, b = prefix;
-	for (;;) {
-		if (*b == 0)
-			return (char *)a;
-		if (*a != *b)
-			return NULL;
-
-		a++, b++;
-	}
-}
-
-static char *startswith_no_case(const char *s, const char *prefix)
-{
-	const char *a, *b;
-
-	assert(s);
-	assert(prefix);
-
-	a = s, b = prefix;
-	for (;;) {
-		if (*b == 0)
-			return (char *)a;
-		if (tolower(*a) != tolower(*b))
-			return NULL;
-
-		a++, b++;
-	}
-}
-
-static char *endswith(const char *s, const char *postfix)
-{
-	size_t sl, pl;
-
-	assert(s);
-	assert(postfix);
-
-	sl = strlen(s);
-	pl = strlen(postfix);
-
-	if (pl == 0)
-		return (char *)s + sl;
-
-	if (sl < pl)
-		return NULL;
-
-	if (memcmp(s + sl - pl, postfix, pl) != 0)
-		return NULL;
-
-	return (char *)s + sl - pl;
-}
-
 static int parse_sec(const char *t, usec_t *usec)
 {
 	 static const struct {
diff --git a/libmount/src/context_mount.c b/libmount/src/context_mount.c
index 94db1ac..4f376e4 100644
--- a/libmount/src/context_mount.c
+++ b/libmount/src/context_mount.c
@@ -21,6 +21,7 @@
 
 #include "linux_version.h"
 #include "mountP.h"
+#include "strutils.h"
 
 /*
  * Kernel supports only one MS_PROPAGATION flag change by one mount(2) syscall,
diff --git a/libmount/src/mountP.h b/libmount/src/mountP.h
index 9e6f4bd..9362c00 100644
--- a/libmount/src/mountP.h
+++ b/libmount/src/mountP.h
@@ -137,11 +137,6 @@ extern int mnt_run_test(struct libmnt_test *tests, int argc, char *argv[]);
 #endif
 
 /* utils.c */
-extern int endswith(const char *s, const char *sx)
-			__attribute__((nonnull));
-extern int startswith(const char *s, const char *sx)
-			__attribute__((nonnull));
-
 extern char *stripoff_last_component(char *path);
 
 extern int mnt_valid_tagname(const char *tagname);
diff --git a/libmount/src/optmap.c b/libmount/src/optmap.c
index 504a5cf..5b25b8f 100644
--- a/libmount/src/optmap.c
+++ b/libmount/src/optmap.c
@@ -58,6 +58,7 @@
  * mount/mount.h.
  */
 #include "mountP.h"
+#include "strutils.h"
 
 /*
  * fs-independent mount flags (built-in MNT_LINUX_MAP)
diff --git a/libmount/src/utils.c b/libmount/src/utils.c
index 4e6a131..9f99241 100644
--- a/libmount/src/utils.c
+++ b/libmount/src/utils.c
@@ -23,37 +23,6 @@
 #include "env.h"
 #include "match.h"
 
-int endswith(const char *s, const char *sx)
-{
-	ssize_t off;
-
-	assert(s);
-	assert(sx);
-
-	off = strlen(s);
-	if (!off)
-		return 0;
-	off -= strlen(sx);
-	if (off < 0)
-		return 0;
-
-        return !strcmp(s + off, sx);
-}
-
-int startswith(const char *s, const char *sx)
-{
-	size_t off;
-
-	assert(s);
-	assert(sx);
-
-	off = strlen(sx);
-	if (!off)
-		return 0;
-
-        return !strncmp(s, sx, off);
-}
-
 int append_string(char **a, const char *b)
 {
 	size_t al, bl;
-- 
1.8.4




-- 
   Sami Kerola
   http://www.iki.fi/kerolasa/

^ permalink raw reply related	[flat|nested] 25+ messages in thread

* Re: [PATCH 15/17] last: use configuration struct
  2013-08-29 10:15   ` Karel Zak
@ 2013-08-29 18:06     ` Sami Kerola
  2013-09-02  9:10       ` Karel Zak
  0 siblings, 1 reply; 25+ messages in thread
From: Sami Kerola @ 2013-08-29 18:06 UTC (permalink / raw)
  To: Karel Zak; +Cc: util-linux

On Thu, Aug 29, 2013 at 12:15:19PM +0200, Karel Zak wrote:
> On Tue, Aug 27, 2013 at 07:06:17PM +0100, Sami Kerola wrote:
> > +	struct last_control ctl;
> > +	int c;
> >  	usec_t p;
> >  
> >  	static const struct option long_opts[] = {
> > @@ -761,6 +775,11 @@ int main(int argc, char **argv)
> >  	      { NULL, 0, NULL, 0 }
> >  	};
> >  
> > +	memset(&ctl, 0, sizeof(struct last_control));
> > +	ctl.showhost = TRUE;
> > +	ctl.name_len = LAST_LOGIN_LEN;
> > +	ctl.domain_len = LAST_DOMAIN_LEN;
> > +
> 
>   struct last_control ctl = {
>          .showhost = TRUE,
>          .name_len = LAST_LOGIN_LEN,
>          .domain_len = LAST_DOMAIN_LEN
>   };
> 
>  is good enough, memset() is unnecessary.

Good point.  Thank you for reminding what the what C99 says about struct
initialization.

http://c0x.coding-guidelines.com/6.7.8.html#1695


>From 74b90f9b404e048ac17c88b24405cdbdcaf56727 Mon Sep 17 00:00:00 2001
From: Sami Kerola <kerolasa@iki.fi>
Date: Tue, 27 Aug 2013 13:15:08 +0100
Subject: [PATCH 17/26] last: use configuration struct
Organization: Lastminute.com

This allows reducing global variables and will minimize number of
arguments for functions making code a little bit easier to read, and
maintain.

Signed-off-by: Sami Kerola <kerolasa@iki.fi>
---
 login-utils/last.c | 211 ++++++++++++++++++++++++++++-------------------------
 1 file changed, 113 insertions(+), 98 deletions(-)

diff --git a/login-utils/last.c b/login-utils/last.c
index 3cbfa06..84ae264 100644
--- a/login-utils/last.c
+++ b/login-utils/last.c
@@ -53,8 +53,40 @@
 # define SHUTDOWN_TIME 254
 #endif
 
+#ifndef LAST_LOGIN_LEN
+# define LAST_LOGIN_LEN 8
+#endif
+
+#ifndef LAST_DOMAIN_LEN
+# define LAST_DOMAIN_LEN 16
+#endif
+
 #define UCHUNKSIZE	16384	/* How much we read at once. */
 
+struct last_control {
+	char lastb;		/* Is this command 'lastb' */
+	char extended;		/* Lots of info */
+	char showhost;		/* Show hostname */
+	char altlist;		/* Hostname at the end */
+	char usedns;		/* Use DNS to lookup the hostname */
+	char useip;		/* Print IP address in number format */
+	char fulltime;		/* Print full dates and times */
+
+	unsigned int name_len;	/* Number of login name characters to print */
+	unsigned int domain_len; /* Number of domain name characters to print */
+	unsigned int maxrecs;	/* Maximum number of records to list */
+
+	char **show;		/* Match search list */
+
+	char **altv;		/* Alternate wtmp files */
+	unsigned int altc;	/* Number of alternative files */
+	unsigned int alti;	/* Index number of the alternative file */
+
+	time_t since;		/* at what time to start displaying the file */
+	time_t until;		/* at what time to stop displaying the file */
+	time_t present;		/* who where present at time_t */
+};
+
 /* Double linked list of struct utmp's */
 struct utmplist {
 	struct utmp ut;
@@ -75,24 +107,15 @@ enum {
 };
 
 /* Global variables */
-static unsigned int maxrecs;	/* Maximum number of records to list. */
 static unsigned int recsdone;	/* Number of records listed */
-static int showhost = 1;	/* Show hostname too? */
-static int altlist;		/* Show hostname at the end. */
-static int usedns;		/* Use DNS to lookup the hostname. */
-static int useip;		/* Print IP address in number format */
-static int fulltime;		/* Print full dates and times */
-static int name_len = 8;	/* Default print 8 characters of name */
-static int domain_len = 16;	/* Default print 16 characters of domain */
-static char **show = NULL;	/* What do they want us to show */
-static char *ufile;		/* Filename of this file */
 static time_t lastdate;		/* Last date we've seen */
 
 /*
  *	Read one utmp entry, return in new format.
  *	Automatically reposition file pointer.
  */
-static int uread(FILE *fp, struct utmp *u, int *quit)
+static int uread(const struct last_control *ctl, FILE *fp, struct utmp *u,
+		 int *quit)
 {
 	static int utsize;
 	static char buf[UCHUNKSIZE];
@@ -119,12 +142,12 @@ static int uread(FILE *fp, struct utmp *u, int *quit)
 			return 0;
 		o = ((fpos - 1) / UCHUNKSIZE) * UCHUNKSIZE;
 		if (fseeko(fp, o, SEEK_SET) < 0) {
-			warn(_("seek failed: %s"), ufile);
+			warn(_("seek failed: %s"), ctl->altv[ctl->alti]);
 			return 0;
 		}
 		bpos = (int)(fpos - o);
 		if (fread(buf, bpos, 1, fp) != 1) {
-			warn(_("read failed: %s"), ufile);
+			warn(_("read failed: %s"), ctl->altv[ctl->alti]);
 			return 0;
 		}
 		fpos = o;
@@ -153,7 +176,7 @@ static int uread(FILE *fp, struct utmp *u, int *quit)
 	 */
 	memcpy(tmp + (-bpos), buf, utsize + bpos);
 	if (fseeko(fp, fpos, SEEK_SET) < 0) {
-		warn(_("seek failed: %s"), ufile);
+		warn(_("seek failed: %s"), ctl->altv[ctl->alti]);
 		return 0;
 	}
 
@@ -161,7 +184,7 @@ static int uread(FILE *fp, struct utmp *u, int *quit)
 	 *	Read another UCHUNKSIZE bytes.
 	 */
 	if (fread(buf, UCHUNKSIZE, 1, fp) != 1) {
-		warn(_("read failed: %s"), ufile);
+		warn(_("read failed: %s"), ctl->altv[ctl->alti]);
 		return 0;
 	}
 
@@ -251,7 +274,7 @@ static int dns_lookup(char *result, int size, int useip, int32_t *a)
 /*
  *	Show one line of information on screen
  */
-static int list(struct utmp *p, time_t t, int what, time_t present)
+static int list(const struct last_control *ctl, struct utmp *p, time_t t, int what)
 {
 	time_t		secs, tmp;
 	char		logintime[32];
@@ -277,9 +300,9 @@ static int list(struct utmp *p, time_t t, int what, time_t present)
 	/*
 	 *	Is this something we wanna show?
 	 */
-	if (show) {
+	if (ctl->show) {
 		char **walk;
-		for (walk = show; *walk; walk++) {
+		for (walk = ctl->show; *walk; walk++) {
 			if (strncmp(p->ut_name, *walk, UT_NAMESIZE) == 0 ||
 			    strcmp(utline, *walk) == 0 ||
 			    (strncmp(utline, "tty", 3) == 0 &&
@@ -293,11 +316,11 @@ static int list(struct utmp *p, time_t t, int what, time_t present)
 	 */
 	tmp = (time_t)p->ut_time;
 
-	if (present && (present < tmp || (0 < t && t < present)))
+	if (ctl->present && (ctl->present < tmp || (0 < t && t < ctl->present)))
 		return 0;
 
 	strcpy(logintime, ctime(&tmp));
-	if (fulltime)
+	if (ctl->fulltime)
 		sprintf(logouttime, "- %s", ctime(&t));
 	else {
 		logintime[16] = 0;
@@ -322,7 +345,7 @@ static int list(struct utmp *p, time_t t, int what, time_t present)
 			break;
 		case R_NOW:
 			length[0] = 0;
-			if (fulltime)
+			if (ctl->fulltime)
 				sprintf(logouttime, "  still logged in");
 			else {
 				sprintf(logouttime, "  still");
@@ -331,7 +354,7 @@ static int list(struct utmp *p, time_t t, int what, time_t present)
 			break;
 		case R_PHANTOM:
 			length[0] = 0;
-			if (fulltime)
+			if (ctl->fulltime)
 				sprintf(logouttime, "  gone - no logout");
 			else {
 				sprintf(logouttime, "   gone");
@@ -354,8 +377,8 @@ static int list(struct utmp *p, time_t t, int what, time_t present)
 	 *	Look up host with DNS if needed.
 	 */
 	r = -1;
-	if (usedns || useip)
-		r = dns_lookup(domain, sizeof(domain), useip, p->ut_addr_v6);
+	if (ctl->usedns || ctl->useip)
+		r = dns_lookup(domain, sizeof(domain), ctl->useip, p->ut_addr_v6);
 	if (r < 0) {
 		len = UT_HOSTSIZE;
 		if (len >= (int)sizeof(domain)) len = sizeof(domain) - 1;
@@ -363,28 +386,28 @@ static int list(struct utmp *p, time_t t, int what, time_t present)
 		strncat(domain, p->ut_host, len);
 	}
 
-	if (showhost) {
-		if (!altlist) {
+	if (ctl->showhost) {
+		if (!ctl->altlist) {
 			len = snprintf(final, sizeof(final),
-				fulltime ?
+				ctl->fulltime ?
 				"%-8.*s %-12.12s %-16.*s %-24.24s %-26.26s %-12.12s\n" :
 				"%-8.*s %-12.12s %-16.*s %-16.16s %-7.7s %-12.12s\n",
-				name_len, p->ut_name, utline,
-				domain_len, domain, logintime, logouttime, length);
+				ctl->name_len, p->ut_name, utline,
+				ctl->domain_len, domain, logintime, logouttime, length);
 		} else {
 			len = snprintf(final, sizeof(final),
-				fulltime ?
+				ctl->fulltime ?
 				"%-8.*s %-12.12s %-24.24s %-26.26s %-12.12s %s\n" :
 				"%-8.*s %-12.12s %-16.16s %-7.7s %-12.12s %s\n",
-				name_len, p->ut_name, utline,
+				ctl->name_len, p->ut_name, utline,
 				logintime, logouttime, length, domain);
 		}
 	} else
 		len = snprintf(final, sizeof(final),
-			fulltime ?
+			ctl->fulltime ?
 			"%-8.*s %-12.12s %-24.24s %-26.26s %-12.12s\n" :
 			"%-8.*s %-12.12s %-16.16s %-7.7s %-12.12s\n",
-			name_len, p->ut_name, utline,
+			ctl->name_len, p->ut_name, utline,
 			logintime, logouttime, length);
 
 #if defined(__GLIBC__)
@@ -403,7 +426,7 @@ static int list(struct utmp *p, time_t t, int what, time_t present)
 		putchar('\n');
 
 	recsdone++;
-	if (maxrecs && recsdone >= maxrecs)
+	if (ctl->maxrecs && ctl->maxrecs <= recsdone)
 		return 1;
 
 	return 0;
@@ -441,8 +464,7 @@ static void __attribute__((__noreturn__)) usage(FILE *out)
 }
 
 
-static void process_wtmp_file(char *ufile, int lastb, int extended,
-			      time_t since, time_t until, time_t present)
+static void process_wtmp_file(const struct last_control *ctl)
 {
 	FILE *fp;		/* Filepointer of wtmp file */
 
@@ -478,8 +500,8 @@ static void process_wtmp_file(char *ufile, int lastb, int extended,
 	/*
 	 * Open the utmp file
 	 */
-	if ((fp = fopen(ufile, "r")) == NULL)
-		err(EXIT_FAILURE, _("cannot open %s"), ufile);
+	if ((fp = fopen(ctl->altv[ctl->alti], "r")) == NULL)
+		err(EXIT_FAILURE, _("cannot open %s"), ctl->altv[ctl->alti]);
 
 	/*
 	 * Optimize the buffer size.
@@ -489,7 +511,7 @@ static void process_wtmp_file(char *ufile, int lastb, int extended,
 	/*
 	 * Read first structure to capture the time field
 	 */
-	if (uread(fp, &ut, NULL) == 1)
+	if (uread(ctl, fp, &ut, NULL) == 1)
 		begintime = ut.ut_time;
 	else {
 		fstat(fileno(fp), &st);
@@ -501,26 +523,26 @@ static void process_wtmp_file(char *ufile, int lastb, int extended,
 	 * Go to end of file minus one structure
 	 * and/or initialize utmp reading code.
 	 */
-	uread(fp, NULL, NULL);
+	uread(ctl, fp, NULL, NULL);
 
 	/*
 	 * Read struct after struct backwards from the file.
 	 */
 	while (!quit) {
 
-		if (uread(fp, &ut, &quit) != 1)
+		if (uread(ctl, fp, &ut, &quit) != 1)
 			break;
 
-		if (since && ut.ut_time < since)
+		if (ctl->since && ut.ut_time < ctl->since)
 			continue;
 
-		if (until && until < ut.ut_time)
+		if (ctl->until && ctl->until < ut.ut_time)
 			continue;
 
 		lastdate = ut.ut_time;
 
-		if (lastb) {
-			quit = list(&ut, ut.ut_time, R_NORMAL, present);
+		if (ctl->lastb) {
+			quit = list(ctl, &ut, ut.ut_time, R_NORMAL);
 			continue;
 		}
 
@@ -566,33 +588,33 @@ static void process_wtmp_file(char *ufile, int lastb, int extended,
 #endif
 		switch (ut.ut_type) {
 		case SHUTDOWN_TIME:
-			if (extended) {
+			if (ctl->extended) {
 				strcpy(ut.ut_line, "system down");
-				quit = list(&ut, lastboot, R_NORMAL, present);
+				quit = list(ctl, &ut, lastboot, R_NORMAL);
 			}
 			lastdown = lastrch = ut.ut_time;
 			down = 1;
 			break;
 		case OLD_TIME:
 		case NEW_TIME:
-			if (extended) {
+			if (ctl->extended) {
 				strcpy(ut.ut_line,
 				ut.ut_type == NEW_TIME ? "new time" :
 					"old time");
-				quit = list(&ut, lastdown, R_TIMECHANGE, present);
+				quit = list(ctl, &ut, lastdown, R_TIMECHANGE);
 			}
 			break;
 		case BOOT_TIME:
 			strcpy(ut.ut_line, "system boot");
-			quit = list(&ut, lastdown, R_REBOOT, present);
+			quit = list(ctl, &ut, lastdown, R_REBOOT);
 			lastboot = ut.ut_time;
 			down = 1;
 			break;
 		case RUN_LVL:
 			x = ut.ut_pid & 255;
-			if (extended) {
+			if (ctl->extended) {
 				sprintf(ut.ut_line, "(to lvl %c)", x);
-				quit = list(&ut, lastrch, R_NORMAL, present);
+				quit = list(ctl, &ut, lastrch, R_NORMAL);
 			}
 			if (x == '0' || x == '6') {
 				lastdown = ut.ut_time;
@@ -615,8 +637,7 @@ static void process_wtmp_file(char *ufile, int lastb, int extended,
 				    UT_LINESIZE) == 0) {
 					/* Show it */
 					if (c == 0) {
-						quit = list(&ut, p->ut.ut_time,
-							R_NORMAL, present);
+						quit = list(ctl, &ut, p->ut.ut_time, R_NORMAL);
 						c = 1;
 					}
 					if (p->next) p->next->prev = p->prev;
@@ -641,7 +662,7 @@ static void process_wtmp_file(char *ufile, int lastb, int extended,
 						c = R_PHANTOM;
 				} else
 					c = whydown;
-				quit = list(&ut, lastboot, c, present);
+				quit = list(ctl, &ut, lastboot, c);
 			}
 			/* FALLTHRU */
 
@@ -687,21 +708,18 @@ static void process_wtmp_file(char *ufile, int lastb, int extended,
 		}
 	}
 
-	printf(_("\n%s begins %s"), basename(ufile), ctime(&begintime));
+	printf(_("\n%s begins %s"), basename(ctl->altv[ctl->alti]), ctime(&begintime));
 	fclose(fp);
 }
 
 int main(int argc, char **argv)
 {
-	int c, i;		/* Scratch */
-	char **altv = NULL;	/* Alternate wtmp files */
-	int altc = 0;		/* Number of alternative files */
-	int lastb = 0;		/* Is this 'lastb' ? */
-	int extended = 0;	/* Lots of info. */
-
-	time_t since = 0;	/* at what time to start displaying the file */
-	time_t until = 0;	/* at what time to stop displaying the file */
-	time_t present = 0;	/* who where present at time_t */
+	struct last_control ctl = {
+		.showhost = TRUE,
+		.name_len = LAST_LOGIN_LEN,
+		.domain_len = LAST_DOMAIN_LEN
+	};
+	int c;
 	usec_t p;
 
 	static const struct option long_opts[] = {
@@ -737,58 +755,55 @@ int main(int argc, char **argv)
 			printf(UTIL_LINUX_VERSION);
 			return EXIT_SUCCESS;
 		case 'R':
-			showhost = 0;
+			ctl.showhost = FALSE;
 			break;
 		case 'x':
-			extended = 1;
+			ctl.extended = TRUE;
 			break;
 		case 'n':
-			maxrecs = strtos32_or_err(optarg, _("failed to parse number"));
+			ctl.maxrecs = strtos32_or_err(optarg, _("failed to parse number"));
 			break;
 		case 'f':
-			if (!altv)
-				altv = xmalloc(sizeof(char *) * argc);
-			altv[altc++] = xstrdup(optarg);
+			if (!ctl.altv)
+				ctl.altv = xmalloc(sizeof(char *) * argc);
+			ctl.altv[ctl.altc++] = xstrdup(optarg);
 			break;
 		case 'd':
-			usedns++;
+			ctl.usedns = TRUE;
 			break;
 		case 'i':
-			useip++;
+			ctl.useip = TRUE;
 			break;
 		case 'a':
-			altlist++;
+			ctl.altlist = TRUE;
 			break;
 		case 'F':
-			fulltime++;
+			ctl.fulltime = TRUE;
 			break;
 		case 'p':
 			if (parse_timestamp(optarg, &p) < 0)
 				errx(EXIT_FAILURE, _("invalid time value \"%s\""), optarg);
-			present = (time_t) (p / 1000000);
+			ctl.present = (time_t) (p / 1000000);
 			break;
 		case 's':
-			since = parsetm(optarg);
-			if (since != (time_t) -1)
-				break;
 			if (parse_timestamp(optarg, &p) < 0)
 				errx(EXIT_FAILURE, _("invalid time value \"%s\""), optarg);
-			since = (time_t) (p / 1000000);
+			ctl.since = (time_t) (p / 1000000);
 			break;
 		case 't':
 			if (parse_timestamp(optarg, &p) < 0)
 				errx(EXIT_FAILURE, _("invalid time value \"%s\""), optarg);
-			until = (time_t) (p / 1000000);
+			ctl.until = (time_t) (p / 1000000);
 			break;
 		case 'w':
-			if (UT_NAMESIZE > name_len)
-				name_len = UT_NAMESIZE;
-			if (UT_HOSTSIZE > domain_len)
-				domain_len = UT_HOSTSIZE;
+			if (ctl.name_len < UT_NAMESIZE)
+				ctl.name_len = UT_NAMESIZE;
+			if (ctl.domain_len < UT_HOSTSIZE)
+				ctl.domain_len = UT_HOSTSIZE;
 			break;
 		case '0': case '1': case '2': case '3': case '4':
 		case '5': case '6': case '7': case '8': case '9':
-			maxrecs = 10 * maxrecs + c - '0';
+			ctl.maxrecs = 10 * ctl.maxrecs + c - '0';
 			break;
 		default:
 			usage(stderr);
@@ -797,25 +812,25 @@ int main(int argc, char **argv)
 	}
 
 	if (optind < argc)
-		show = argv + optind;
+		ctl.show = argv + optind;
 
 	/*
 	 * Which file do we want to read?
 	 */
-	lastb = !strcmp(program_invocation_short_name, "lastb");
-	if (!altc) {
-		altv = xmalloc(sizeof(char *));
-		if (lastb)
-			altv[0] = xstrdup(_PATH_BTMP);
+	ctl.lastb = !strcmp(program_invocation_short_name, "lastb");
+	if (!ctl.altc) {
+		ctl.altv = xmalloc(sizeof(char *));
+		if (ctl.lastb)
+			ctl.altv[0] = xstrdup(_PATH_BTMP);
 		else
-			altv[0] = xstrdup(_PATH_WTMP);
-		altc++;
+			ctl.altv[0] = xstrdup(_PATH_WTMP);
+		ctl.altc++;
 	}
 
-	for (i = 0; i < altc; i++) {
-		process_wtmp_file(altv[i], lastb, extended, since, until, present);
-		free(altv[i]);
+	for (; ctl.alti < ctl.altc; ctl.alti++) {
+		process_wtmp_file(&ctl);
+		free(ctl.altv[ctl.alti]);
 	}
-	free(altv);
+	free(ctl.altv);
 	return EXIT_SUCCESS;
 }
-- 
1.8.4



-- 
   Sami Kerola
   http://www.iki.fi/kerolasa/

^ permalink raw reply related	[flat|nested] 25+ messages in thread

* Re: [PATCH 15/17] last: use configuration struct
  2013-08-29 18:06     ` Sami Kerola
@ 2013-09-02  9:10       ` Karel Zak
  0 siblings, 0 replies; 25+ messages in thread
From: Karel Zak @ 2013-09-02  9:10 UTC (permalink / raw)
  To: Sami Kerola; +Cc: util-linux

On Thu, Aug 29, 2013 at 07:06:33PM +0100, Sami Kerola wrote:
> +struct last_control {
> +	char lastb;		/* Is this command 'lastb' */
> +	char extended;		/* Lots of info */
> +	char showhost;		/* Show hostname */
> +	char altlist;		/* Hostname at the end */
> +	char usedns;		/* Use DNS to lookup the hostname */
> +	char useip;		/* Print IP address in number format */
> +	char fulltime;		/* Print full dates and times */

 BTW, it's C so use bit array next time :-)

     unsigned int lastb : 1,
                  ....

    Karel

-- 
 Karel Zak  <kzak@redhat.com>
 http://karelzak.blogspot.com

^ permalink raw reply	[flat|nested] 25+ messages in thread

* Re: [PATCH 00/17] pull: term-utils changes with emphasis on last(1)
  2013-08-27 18:06 [PATCH 00/17] pull: term-utils changes with emphasis on last(1) Sami Kerola
                   ` (15 preceding siblings ...)
  2013-08-27 18:06 ` [PATCH 17/17] docs: add --time-format to last(1) manual page Sami Kerola
@ 2013-09-02  9:59 ` Karel Zak
  16 siblings, 0 replies; 25+ messages in thread
From: Karel Zak @ 2013-09-02  9:59 UTC (permalink / raw)
  To: Sami Kerola; +Cc: util-linux

On Tue, Aug 27, 2013 at 07:06:03PM +0100, Sami Kerola wrote:
>   git://github.com/kerolasa/lelux-utiliteetit.git 2013wk34

 Merged. Thanks!

-- 
 Karel Zak  <kzak@redhat.com>
 http://karelzak.blogspot.com

^ permalink raw reply	[flat|nested] 25+ messages in thread

end of thread, other threads:[~2013-09-02  9:59 UTC | newest]

Thread overview: 25+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2013-08-27 18:06 [PATCH 00/17] pull: term-utils changes with emphasis on last(1) Sami Kerola
2013-08-27 18:06 ` [PATCH 02/17] wall: send message also to sessions opened by user 'sleeper' Sami Kerola
2013-08-27 18:06 ` [PATCH 03/17] last: fix typo Sami Kerola
2013-08-27 18:06 ` [PATCH 04/17] wall: sync usage() with howto-usage-function.txt Sami Kerola
2013-08-27 18:06 ` [PATCH 05/17] mesg: " Sami Kerola
2013-08-27 18:06 ` [PATCH 06/17] wall: line wrap at column 79 also when line has tab chars Sami Kerola
2013-08-27 18:06 ` [PATCH 07/17] docs: add --present to last(1) manual page Sami Kerola
2013-08-27 18:06 ` [PATCH 08/17] docs: add note to wall(1) about sessions which will not get message Sami Kerola
2013-08-27 18:06 ` [PATCH 09/17] write: change determination can user write to a terminal Sami Kerola
2013-08-27 18:06 ` [PATCH 10/17] lib/time-util: copy time parsing functions from systemd Sami Kerola
2013-08-29 10:11   ` Karel Zak
2013-08-29 18:02     ` Sami Kerola
2013-08-27 18:06 ` [PATCH 11/17] last: parse easy to use time formats Sami Kerola
2013-08-29 10:09   ` Karel Zak
2013-08-29 18:01     ` Sami Kerola
2013-08-27 18:06 ` [PATCH 12/17] last: add --since time spec option Sami Kerola
2013-08-27 18:06 ` [PATCH 13/17] docs: add --since and time option formats to last(1) manual Sami Kerola
2013-08-27 18:06 ` [PATCH 14/17] last: fix --present option logic error Sami Kerola
2013-08-27 18:06 ` [PATCH 15/17] last: use configuration struct Sami Kerola
2013-08-29 10:15   ` Karel Zak
2013-08-29 18:06     ` Sami Kerola
2013-09-02  9:10       ` Karel Zak
2013-08-27 18:06 ` [PATCH 16/17] last: add --time-format with iso-8601 format Sami Kerola
2013-08-27 18:06 ` [PATCH 17/17] docs: add --time-format to last(1) manual page Sami Kerola
2013-09-02  9:59 ` [PATCH 00/17] pull: term-utils changes with emphasis on last(1) Karel Zak

This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.