]> granicus.if.org Git - procps-ng/commitdiff
ps: Add configurable date format for lstart field
authorCraig Small <csmall@dropbear.xyz>
Wed, 18 Jan 2023 06:46:06 +0000 (17:46 +1100)
committerCraig Small <csmall@dropbear.xyz>
Wed, 18 Jan 2023 06:46:06 +0000 (17:46 +1100)
The lstart field has been converted to use the strftime()
function so that it uses the locale. A new option -D
allows the user to define the format that would want this
field to show.

This may mean the field will be longer than it should be,
especially for French locales and the user defined field,
but the field length can be specified too.

---

This commit started off making all the relevant fields use the
locale correctly so it could solve #226 as well. The issue
is there an implied restriction (or not) around
strftime("%b") and probably strftime("%a") for abbrievated month
and day names respectively.

English, and some/most other languages put an additional
restriction that all abbreviations are 3 characters long.
The problem is, not all languages do this.

French is a good example:
janv. févr. mars avril mai juin juil. août sept. oct. nov. déc.

Maybe strip the . at the end?
 That helps for some months, not all
Maybe take the first three characters?
 Several wide languages will have big issues
Maybe convert wide, get wcslen then use that.
 Even after that June "juin" and July "juil" are both "jui".

So, anything that uses the month (bsdstart,start) use ctime which
doesn't use locale. That solves the length issue.

stime does, which means it has this issue but its been like that
for years. You get stuff like this:

janv.13 482261
00:00 1151918
 2022 1458628
06:12 1957584

The only way to fix that would be to
 a)Make the field two characters longer
 b)Convert it back to ctime() which means everyone else
   loses.

This could have be oh-so easy if everyone made %b and %a three
(wide) characters everywhere.

References:
 procps-ng/procps#228
 procps-ng/procps#226

Signed-off-by: Craig Small <csmall@dropbear.xyz>
man/ps.1
src/ps/common.h
src/ps/global.c
src/ps/help.c
src/ps/output.c
src/ps/parser.c

index 7f933fcd3f935f9cc289dc15d96e6b7a10cf1dd7..095425f40445a22ca090f9ff2594b7b2ee5ce8fa 100644 (file)
--- a/man/ps.1
+++ b/man/ps.1
@@ -1,10 +1,15 @@
-'\" t
-.\" (The preceding line is a note to broken versions of man to tell
-.\" Man page for ps.
-.\" Quick hack conversion by Albert Cahalan, 1998.
-.\" Licensed under version 2 of the Gnu General Public License.
 .\"
-.TH PS "1" "2023-01-16" "procps-ng" "User Commands"
+.\" Copyright      1998 Albert Cahalan
+.\"           2011-2023 Jim Warner <james.warner@comcast.net>
+.\"           2011-2023 Craig Small <csmall@dropbear.xyz>
+.\"
+.\" This program is free software; you can redistribute it and/or modify
+.\" it under the terms of the GNU General Public License as published by
+.\" the Free Software Foundation; either version 2 of the License, or
+.\" (at your option) any later version.
+.\"
+.\"
+.TH PS "1" "2023-01-18" "procps-ng" "User Commands"
 .\"
 .\" To render this page:
 .\"    groff -t -b -man -X -P-resolution -P100 -Tps ps.1 &
@@ -598,6 +603,16 @@ Set screen width.
 .B \-\-cumulative
 Include some dead child process data (as a sum with the parent).
 .TP
+.TP
+.BI \-D \ format
+Set the date format of the \fBlstart\fR field to \fIformat\fR. This format is parsed
+by
+.BR strftime (3)
+and should be a maximum of 24 characters to not mis-align columns.
+.TP
+.BI \-\-date-format \ format
+Identical to \fB\-D\fR.
+.TP
 .B e
 Show the environment after the command.
 .TP
@@ -1385,8 +1400,8 @@ the
 T}
 
 lstart STARTED T{
-time the command started.  See also
-.BR bsdstart , \ start , \ start_time ", and" \ stime .
+time the command started. This will be in the form "DDD mmm HH:MM:SS YYY"
+unless changed by the \fB\-D\fR option.
 T}
 
 lsession       SESSION T{
@@ -2064,10 +2079,18 @@ unix98  standard
 .TE
 .PP
 .PP
+.SH BUGS
+The fields \fBbsdstart\fR and \fBstart\fR will only show the abbreviated
+month name in English. The fields \fBlstart\fR and \fBstime\fR will show
+the abbreviated month name in the configured locale but may exceed the
+column width due to the different lengths for abbreviated month and day
+names across languages.
+.PP
 .SH "SEE ALSO"
 .BR pgrep (1),
 .BR pstree (1),
 .BR top (1),
+.BR strftime (3),
 .BR proc (5).
 .PP
 .PP
index 26bd38c01a5141a1c6088e64d3f5496cc3998bec..6e416a3419ceafde455b2ec0bda35ecfe8fd303c 100644 (file)
@@ -451,6 +451,7 @@ extern int             header_gap;
 extern int             header_type; /* none, single, multi... */
 extern int             include_dead_children;
 extern int             lines_to_next_header;
+extern char           *lstart_format;
 extern int             max_line_width;
 extern int             negate_selection;
 extern int             page_size;  // "int" for math reasons?
index db67a78229bcd6691525e87d0a53ecae07438885..826ca92438abd4b0396d2f018bc9322d0e6c04b4 100644 (file)
@@ -183,6 +183,7 @@ int             header_gap = -1;
 int             header_type = -1;
 int             include_dead_children = -1;
 int             lines_to_next_header = -1;
+char           *lstart_format = NULL;
 int             negate_selection = -1;
 int             running_only = -1;
 int             page_size = -1;  // "int" for math reasons?
index b64833d37e59c5d05417f3a53ff4deec716ea25f..dfd602d16ea3679bfa5187b7f907926b595625f8 100644 (file)
@@ -126,6 +126,7 @@ void do_help (const char *opt, int rc) {
   }
   if (section == HELP_OUT || section == HELP_ALL) {
     fputs(_("\nOutput formats:\n"), out);
+    fputs(_(" -D <format>          date format for lstart\n"), out);
     fputs(_(" -F                   extra full\n"), out);
     fputs(_(" -f                   full-format, including command lines\n"), out);
     fputs(_("  f, --forest         ascii art process tree\n"), out);
index 26001a6cc52023522eced72af3a4773851fad4a4..3074549c5dba4c701eadcecf76b552acbf163a1d 100644 (file)
@@ -1044,11 +1044,21 @@ setREL1(VM_RSS)
   return snprintf(outbuf, COLWID, "%2u.%u", (unsigned)(pmem/10), (unsigned)(pmem%10));
 }
 
+// Format cannot be %c as the length changes depending on locale
+#define DEFAULT_LSTART_FORMAT "%a %b %e %H:%M:%S %Y"
 static int pr_lstart(char *restrict const outbuf, const proc_t *restrict const pp){
-  time_t t;
+    time_t t;
+    struct tm start_time;
+    size_t len;
 setREL1(TICS_BEGAN)
-  t = boot_time() + rSv(TICS_BEGAN, ull_int, pp) / Hertz;
-  return snprintf(outbuf, COLWID, "%24.24s", ctime(&t));
+    t = boot_time() + rSv(TICS_BEGAN, ull_int, pp) / Hertz;
+    if (localtime_r(&t, &start_time) == NULL)
+        return 0;
+    len = strftime(outbuf, COLWID,
+            (lstart_format?lstart_format:DEFAULT_LSTART_FORMAT), &start_time);
+    if (len <= 0 || len >= COLWID)
+        outbuf[len = 0] = '\0';
+  return len;
 }
 
 /* Unix98 specifies a STIME header for a column that shows the start
index b836241459f30e8252daaccbb109a0e37369d3d5..544f7369fc0c3db5c046454990ba8de16c9e2ee6 100644 (file)
@@ -244,6 +244,13 @@ static const char *parse_sysv_option(void){
       if(err) return err;
       selection_list->typecode = SEL_COMM;
       return NULL; /* can't have any more options */
+    case 'D':
+      trace("-D sets lstart date format\n");
+      arg = get_opt_arg();
+      if (!arg) return _("date format must follow -D");
+      if (lstart_format) free(lstart_format);
+      lstart_format = strdup(arg);
+      break;
     case 'F':  /* DYNIX/ptx -f plus sz,rss,psr=ENG between c and stime */
       trace("-F does fuller listing\n");
       format_modifiers |= FM_F;
@@ -794,6 +801,7 @@ static const char *parse_gnu_option(void){
   {"columns",       &&case_columns},
   {"context",       &&case_context},
   {"cumulative",    &&case_cumulative},
+  {"date-format",   &&case_dateformat},
   {"deselect",      &&case_deselect},    /* -N */
   {"forest",        &&case_forest},      /* f -H */
   {"format",        &&case_format},
@@ -881,6 +889,12 @@ static const char *parse_gnu_option(void){
     if(s[sl]) return _("option --cumulative does not take an argument");
     include_dead_children = 1;
     return NULL;
+  case_dateformat:
+    arg=grab_gnu_arg();
+    if (!arg) return _("date format must follow --date-format");
+    if (lstart_format) free(lstart_format);
+    lstart_format = strdup(arg);
+    return NULL;
   case_deselect:
     trace("--deselect\n");
     if(s[sl]) return _("option --deselect does not take an argument");