mirror of
git://git.code.sf.net/p/cdesktopenv/code
synced 2025-03-09 15:50:02 +00:00
Path-bound builtins on ksh (such as /opt/ast/bin/cat) break some
basic assumptions about paths in the shell that should hold true,
e.g., that a path output by whence -p or command -v should actually
point to an executable command. This commit should fix the
following:
1. Path-bound built-ins (such as /opt/ast/bin/cat) can now be
executed by invoking the canonical path (independently of the
value of $PATH), so the following will now work as expected:
$ /opt/ast/bin/cat --version
version cat (AT&T Research) 2012-05-31
$ (PATH=/opt/ast/bin:$PATH; "$(whence -p cat)" --version)
version cat (AT&T Research) 2012-05-31
In the event an external command by that path exists, the
path-bound builtin will now override it when invoked using the
canonical path. To invoke a possible external command at that
path, you can still use a non-canonical path, e.g.:
/opt//ast/bin/cat or /opt/ast/./bin/cat
2. Path-bound built-ins will now also be found on a PATH set
locally using an assignment preceding the command, so something
like the following will now work as expected:
$ PATH=/opt/ast/bin cat --version
version cat (AT&T Research) 2012-05-31
The builtin is not found by sh_exec() because the search for
builtins happens long before invocation-local preceding
assignments are processsed. This only happens in sh_ntfork(),
before forking, or in sh_fork(), after forking. Both sh_ntfork()
and sh_fork() call path_spawn() to do the actual path search, so
a check there will cover both cases.
This does mean the builtin will be run in the forked child if
sh_fork() is used (which is the case on interactive shells with
job.jobcontrol set, or always after compiling with SHOPT_SPAWN
disabled). Searching for it before forking would mean
fundamentally redesigning that function to be basically like
sh_ntfork(), so this is hard to avoid.
src/cmd/ksh93/sh/path.c: path_spawn():
- Before doing anything else, check if the passed path appears in
the builtins tree as a pathbound builtin. If so, run it. Since a
builtin will only be found if a preceding PATH assignment
temporarily changed the PATH, and that assignment is currently in
effect, we can just sh_run() the builtin so a nested sh_exec()
invocation will find and run it.
- If 'spawn' is not set (i.e. we must return), set errno to 0 and
return -2. See the change to sh_ntfork() below.
src/cmd/ksh93/sh/xec.c:
- sh_exec(): When searching for built-ins and the restricted option
isn't active, also search bltin_tree for names beginning with a
slash.
- sh_ntfork(): Only throw an error if the PID value returned is
exactly -1. This allows path_spawn() to return -2 after running a
built-in to tell sh_ntfork() to do the right things to restore
state.
src/cmd/ksh93/sh/parse.c: simple():
- When searching for built-ins at parse time, only exclude names
containing a slash if the restricted option is active. This
allows finding pointers to built-ins invoked by literal path like
/opt/ast/bin/cat, as long as that does not result from an
expansion. This is not actually necessary as sh_exec() will also
cover this case, but it is an optimisation.
src/lib/libcmd/getconf.c:
- Replace convoluted deferral to external command by a simple
invocation of the path to the native getconf command determined
at compile time (by src/lib/libast/comp/conf.sh). Based on:
https://github.com/ksh93/ksh/issues/138#issuecomment-816384871
If there is ever a system that has /opt/ast/bin/getconf as its
default native external 'getconf', then there would still be an
infinite recursion crash, but this seems extremely unlikely.
Resolves: https://github.com/ksh93/ksh/issues/138
528 lines
15 KiB
C
528 lines
15 KiB
C
/***********************************************************************
|
|
* *
|
|
* This software is part of the ast package *
|
|
* Copyright (c) 1992-2012 AT&T Intellectual Property *
|
|
* and is licensed under the *
|
|
* Eclipse Public License, Version 1.0 *
|
|
* by AT&T Intellectual Property *
|
|
* *
|
|
* A copy of the License is available at *
|
|
* http://www.eclipse.org/org/documents/epl-v10.html *
|
|
* (with md5 checksum b35adb5213ca9657e911e9befb180842) *
|
|
* *
|
|
* Information and Software Systems Research *
|
|
* AT&T Research *
|
|
* Florham Park NJ *
|
|
* *
|
|
* Glenn Fowler <gsf@research.att.com> *
|
|
* David Korn <dgk@research.att.com> *
|
|
* *
|
|
***********************************************************************/
|
|
#pragma prototyped
|
|
/*
|
|
* Glenn Fowler
|
|
* AT&T Research
|
|
*
|
|
* date -- set/display date
|
|
*/
|
|
|
|
static const char usage[] =
|
|
"[-?\n@(#)$Id: date (AT&T Research) 2011-01-27 $\n]"
|
|
"[--catalog?" ERROR_CATALOG "]"
|
|
"[+NAME?date - set/list/convert dates]"
|
|
"[+DESCRIPTION?\bdate\b sets the current date and time (with appropriate"
|
|
" privileges, provided the shell isn't in restricted mode), lists"
|
|
" the current date or file dates, or converts dates.]"
|
|
"[+?Most common \adate\a forms are recognized, including those for"
|
|
" \bcrontab\b(1), \bls\b(1), \btouch\b(1), and the default"
|
|
" output from \bdate\b itself.]"
|
|
"[+?If the \adate\a operand consists of 4, 6, 8, 10 or 12 digits followed"
|
|
" by an optional \b.\b and two digits then it is interpreted as:"
|
|
" \aHHMM.SS\a, \addHHMM.SS\a, \ammddHHMM.SS\a, \ammddHHMMyy.SS\a or"
|
|
" \ayymmddHHMM.SS\a, or \ammddHHMMccyy.SS\a or \accyymmddHHMM.SS\a."
|
|
" Conflicting standards and practice allow a leading or trailing"
|
|
" 2 or 4 digit year for the 10 and 12 digit forms; the X/Open trailing"
|
|
" form is used to disambiguate (\btouch\b(1) uses the leading form.)"
|
|
" Avoid the 10 digit form to avoid confusion. The digit fields are:]{"
|
|
" [+cc?Century - 1, 19-20.]"
|
|
" [+yy?Year in century, 00-99.]"
|
|
" [+mm?Month, 01-12.]"
|
|
" [+dd?Day of month, 01-31.]"
|
|
" [+HH?Hour, 00-23.]"
|
|
" [+MM?Minute, 00-59.]"
|
|
" [+SS?Seconds, 00-60.]"
|
|
"}"
|
|
"[+?If more than one \adate\a operand is specified then:]{"
|
|
" [+1.?Each operand sets the reference date for the next"
|
|
" operand.]"
|
|
" [+2.?The date is listed for each operand.]"
|
|
" [+3.?The system date is not set.]"
|
|
"}"
|
|
|
|
"[a:access-time|atime?List file argument access times.]"
|
|
"[c:change-time|ctime?List file argument change times.]"
|
|
"[d:date?Use \adate\a as the current date and do not set the system"
|
|
" clock.]:[date]"
|
|
"[e:epoch?Output the date in seconds since the epoch."
|
|
" Equivalent to \b--format=%s\b.]"
|
|
"[E:elapsed?Interpret pairs of arguments as start and stop dates, sum the"
|
|
" differences between all pairs, and list the result as a"
|
|
" \bfmtelapsed\b(3) elapsed time on the standard output. If there are"
|
|
" an odd number of arguments then the last time argument is differenced"
|
|
" with the current time.]"
|
|
"[f:format?Output the date according to the \bstrftime\b(3) \aformat\a."
|
|
" For backwards compatibility, a first argument of the form"
|
|
" \b+\b\aformat\a is equivalent to \b-f\b format."
|
|
" \aformat\a is in \bprintf\b(3) style, where %\afield\a names"
|
|
" a fixed size field, zero padded if necessary,"
|
|
" and \\\ac\a and \\\annn\a sequences are as in C. Invalid"
|
|
" %\afield\a specifications and all other characters are copied"
|
|
" without change. \afield\a may be preceded by \b%-\b to turn off"
|
|
" padding or \b%_\b to pad with space, otherwise numeric fields"
|
|
" are padded with \b0\b and string fields are padded with space."
|
|
" \afield\a may also be preceded by \bE\b for alternate era"
|
|
" representation or \bO\b for alternate digit representation (if"
|
|
" supported by the current locale.) Finally, an integral \awidth\a"
|
|
" preceding \afield\a truncates the field to \awidth\a characters."
|
|
" The fields are:]:[format]{"
|
|
" [+%?% character]"
|
|
" [+a?abbreviated weekday name]"
|
|
" [+A?full weekday name]"
|
|
" [+b?abbreviated month name]"
|
|
" [+B?full month name]"
|
|
" [+c?\bctime\b(3) style date without the trailing newline]"
|
|
" [+C?2-digit century]"
|
|
" [+d?day of month number]"
|
|
" [+D?date as \amm/dd/yy\a]"
|
|
" [+e?blank padded day of month number]"
|
|
" [+f?print a date with the format \b%Y.%m.%d-%H:%M:%S\b]"
|
|
" [+F?ISO 8601:2000 standard date format; equivalent to \b%Y-%m-%d\b]"
|
|
" [+g?\bls\b(1) \b-l\b recent date with \ahh:mm\a]"
|
|
" [+G?\bls\b(1) \b-l\b distant date with \ayyyy\a]"
|
|
" [+h?abbreviated month name]"
|
|
" [+H?24-hour clock hour, zero-padded]"
|
|
" [+i?international \bdate\b(1) date with time zone type name]"
|
|
" [+I?12-hour clock hour, zero-padded]"
|
|
" [+j?1-offset Julian date]"
|
|
" [+J?0-offset Julian date]"
|
|
" [+k?24-hour clock hour, blank-padded]"
|
|
" [+K?all numeric date; equivalent to \b%Y-%m-%d+%H:%M:%S\b; \b%_[EO]]K\b for space separator, %OK adds \b.%N\b, \b%EK\b adds \b%.N%z\b, \b%_EK\b adds \b.%N %z\b]"
|
|
" [+l?12-hour clock hour, blank-padded]"
|
|
" [+L?locale default date format]"
|
|
" [+m?month number]"
|
|
" [+M?minutes]"
|
|
" [+n?newline character]"
|
|
" [+N?nanoseconds 000000000-999999999]"
|
|
" [+p?meridian (e.g., \bAM\b or \bPM\b)]"
|
|
" [+P?lowercase meridian (e.g., \bam\b or \bpm\b)]"
|
|
" [+q?quarter of the year]"
|
|
" [+Q?\a<del>recent<del>distant<del>\a: \a<del>\a is a unique"
|
|
" delimiter character; \arecent\a format for recent"
|
|
" dates, \adistant\a format otherwise]"
|
|
" [+r?12-hour time as \ahh:mm:ss meridian\a]"
|
|
" [+R?24-hour time as \ahh:mm\a]"
|
|
" [+s?number of seconds since the epoch; \a.prec\a preceding"
|
|
" \bs\b appends \aprec\a nanosecond digits, \b9\b if"
|
|
" \aprec\a is omitted]"
|
|
" [+S?seconds 00-60]"
|
|
" [+t?tab character]"
|
|
" [+T?24-hour time as \ahh:mm:ss\a]"
|
|
" [+u?weekday number 1(Monday)-7]"
|
|
" [+U?week number with Sunday as the first day]"
|
|
" [+V?ISO week number (i18n is \afun\a)]"
|
|
" [+w?weekday number 0(Sunday)-6]"
|
|
" [+W?week number with Monday as the first day]"
|
|
" [+x?locale date style that includes month, day and year]"
|
|
" [+X?locale time style that includes hours and minutes]"
|
|
" [+y?2-digit year (you'll be sorry)]"
|
|
" [+Y?4-digit year]"
|
|
" [+z?time zone \aSHHMM\a west of GMT offset where S is"
|
|
" \b+\b or \b-\b, use pad _ for \aSHH:MM\a]"
|
|
" [+Z?time zone name]"
|
|
" [+=[=]][-+]]flag?set (default or +) or clear (-) \aflag\a"
|
|
" for the remainder of \aformat\a, or for the remainder"
|
|
" of the process if \b==\b is specified. \aflag\a may be:]{"
|
|
" [+l?enable leap second adjustments]"
|
|
" [+n?convert \b%S\b as \b%S.%N\b]"
|
|
" [+u?UTC time zone]"
|
|
" }"
|
|
" [+#?equivalent to %s]"
|
|
" [+??alternate?use \aalternate\a format if a default format"
|
|
" override has not been specified, e.g., \bls\b(1) uses"
|
|
" \"%?%l\"; export TM_OPTIONS=\"format='\aoverride\a'\""
|
|
" to override the default]"
|
|
"}"
|
|
"[i:incremental|adjust?Set the system time in incremental adjustments to"
|
|
" avoid complete time shift shock. Negative adjustments still maintain"
|
|
" monotonic increasing time. Not available on all systems.]"
|
|
"[L:last?List only the last time for multiple \adate\a operands.]"
|
|
"[l:leap-seconds?Include leap seconds in time calculations. Leap seconds"
|
|
" after the ast library release date are not accounted for.]"
|
|
"[m:modify-time|mtime?List file argument modify times.]"
|
|
"[n!:network?Set network time.]"
|
|
"[p:parse?Add \aformat\a to the list of \bstrptime\b(3) parse conversion"
|
|
" formats. \aformat\a follows the same conventions as the"
|
|
" \b--format\b option, with the addition of these format"
|
|
" fields:]:[format]{"
|
|
" [+|?If the format failed before this point then restart"
|
|
" the parse with the remaining format.]"
|
|
" [+&?Call the \btmdate\b(3) heuristic parser. This is"
|
|
" is the default when \b--parse\b is omitted.]"
|
|
"}"
|
|
"[R:rfc-2822?List date and time in RFC 2822 format "
|
|
"(%a, %-e %h %Y %H:%M:%S %z).]"
|
|
"[T:rfc-3339?List date and time in RFC 3339 format according to "
|
|
"\atype\a:]:[type]"
|
|
"{"
|
|
"[d:date?(%Y-%m-%d)]"
|
|
"[s:seconds?(%Y-%m-%d %H:%M:%S%_z)]"
|
|
"[n:ns|nanoseconds?(%Y-%m-%d %H:%M:%S.%N%_z)]"
|
|
"}"
|
|
"[s:show?Show the date without setting the system time.]"
|
|
"[u:utc|gmt|zulu|universal?Output dates in \acoordinated universal time\a (UTC).]"
|
|
"[U:unelapsed?Interpret each argument as \bfmtelapsed\b(3) elapsed"
|
|
" time and list the \bstrelapsed\b(3) 1/\ascale\a seconds.]#[scale]"
|
|
"[z:list-zones?List the known time zone table and exit. The table columns"
|
|
" are: country code, standard zone name, savings time zone name,"
|
|
" minutes west of \bUTC\b, and savings time minutes offset. Blank"
|
|
" or empty entries are listed as \b-\b.]"
|
|
|
|
"\n"
|
|
"\n[ +format | date ... | file ... ]\n"
|
|
"\n"
|
|
|
|
"[+SEE ALSO?\bcrontab\b(1), \bls\b(1), \btouch\b(1), \bfmtelapsed\b(3),"
|
|
" \bstrftime\b(3), \bstrptime\b(3), \btm\b(3)]"
|
|
;
|
|
|
|
#include <cmd.h>
|
|
#include <ls.h>
|
|
#include <proc.h>
|
|
#include <tmx.h>
|
|
#include <times.h>
|
|
|
|
typedef struct Fmt
|
|
{
|
|
struct Fmt* next;
|
|
char* format;
|
|
} Fmt_t;
|
|
|
|
/*
|
|
* set the system clock
|
|
* the standards wimped out here
|
|
*/
|
|
|
|
static int
|
|
settime(Shbltin_t* context, const char* cmd, Time_t now, int adjust, int network)
|
|
{
|
|
char** argv;
|
|
char* args[7];
|
|
char buf[1024];
|
|
|
|
if (!adjust && !network)
|
|
return tmxsettime(now);
|
|
argv = args;
|
|
*argv++ = "command";
|
|
*argv++ = "-px";
|
|
*argv++ = "date";
|
|
if (streq(astconf("UNIVERSE", NiL, NiL), "att"))
|
|
{
|
|
tmxfmt(buf, sizeof(buf), "%m%d%H" "%M%Y.%S", now);
|
|
if (adjust)
|
|
*argv++ = "-a";
|
|
}
|
|
else
|
|
{
|
|
#if __FreeBSD__ || __NetBSD__ || __OpenBSD__ || __bsdi__ || __DragonFly__
|
|
tmxfmt(buf, sizeof(buf), "%Y%m%d%H" "%M.%S", now);
|
|
#else
|
|
tmxfmt(buf, sizeof(buf), "%m%d%H" "%M%Y.%S", now);
|
|
#endif
|
|
if (network)
|
|
*argv++ = "-n";
|
|
if (tm_info.flags & TM_UTC)
|
|
*argv++ = "-u";
|
|
}
|
|
*argv++ = buf;
|
|
*argv = 0;
|
|
if (!sh_run(context, argv - args, args))
|
|
return 0;
|
|
return -1;
|
|
}
|
|
|
|
/*
|
|
* convert s to Time_t with error checking
|
|
*/
|
|
|
|
static Time_t
|
|
convert(register Fmt_t* f, char* s, Time_t now)
|
|
{
|
|
char* t;
|
|
char* u;
|
|
|
|
do
|
|
{
|
|
now = tmxscan(s, &t, f->format, &u, now, 0);
|
|
if (!*t && (!f->format || !*u))
|
|
break;
|
|
} while (f = f->next);
|
|
if (!f || *t)
|
|
error(3, "%s: invalid date specification", f ? t : s);
|
|
return now;
|
|
}
|
|
|
|
int
|
|
b_date(int argc, register char** argv, Shbltin_t* context)
|
|
{
|
|
register int n;
|
|
register char* s;
|
|
register Fmt_t* f;
|
|
char* t;
|
|
unsigned long u;
|
|
Time_t now;
|
|
Time_t ts;
|
|
Time_t te;
|
|
Time_t e;
|
|
char buf[1024];
|
|
Fmt_t* fmts;
|
|
Fmt_t fmt;
|
|
struct stat st;
|
|
|
|
char* cmd = argv[0]; /* original command path */
|
|
char* format = 0; /* tmxfmt() format */
|
|
char* string = 0; /* date string */
|
|
int elapsed = 0; /* args are start/stop pairs */
|
|
int filetime = 0; /* use this st_ time field */
|
|
int increment = 0; /* incrementally adjust time */
|
|
int last = 0; /* display the last time arg */
|
|
Tm_zone_t* listzones = 0; /* known time zone table */
|
|
int network = 0; /* don't set network time */
|
|
int show = 0; /* show date and don't set */
|
|
int unelapsed = 0; /* fmtelapsed() => strelapsed */
|
|
|
|
cmdinit(argc, argv, context, ERROR_CATALOG, 0);
|
|
tm_info.flags = TM_DATESTYLE;
|
|
fmts = &fmt;
|
|
fmt.format = "";
|
|
fmt.next = 0;
|
|
for (;;)
|
|
{
|
|
switch (optget(argv, usage))
|
|
{
|
|
case 'a':
|
|
case 'c':
|
|
case 'm':
|
|
filetime = opt_info.option[1];
|
|
continue;
|
|
case 'd':
|
|
string = opt_info.arg;
|
|
show = 1;
|
|
continue;
|
|
case 'e':
|
|
format = "%s";
|
|
continue;
|
|
case 'E':
|
|
elapsed = 1;
|
|
continue;
|
|
case 'f':
|
|
format = opt_info.arg;
|
|
continue;
|
|
case 'i':
|
|
increment = 1;
|
|
continue;
|
|
case 'l':
|
|
tm_info.flags |= TM_LEAP;
|
|
continue;
|
|
case 'L':
|
|
last = 1;
|
|
continue;
|
|
case 'n':
|
|
network = 1;
|
|
continue;
|
|
case 'p':
|
|
if (!(f = newof(0, Fmt_t, 1, 0)))
|
|
{
|
|
error(ERROR_SYSTEM|3, "out of memory [format]");
|
|
UNREACHABLE();
|
|
}
|
|
f->next = fmts;
|
|
f->format = opt_info.arg;
|
|
fmts = f;
|
|
continue;
|
|
case 'R':
|
|
format = "%a, %-e %h %Y %H:%M:%S %z";
|
|
continue;
|
|
case 's':
|
|
show = 1;
|
|
continue;
|
|
case 'T':
|
|
switch (opt_info.num)
|
|
{
|
|
case 'd':
|
|
format = "%Y-%m-%d";
|
|
continue;
|
|
case 'n':
|
|
format = "%Y-%m-%d %H:%M:%S.%N%_z";
|
|
continue;
|
|
case 's':
|
|
format = "%Y-%m-%d %H:%M:%S%_z";
|
|
continue;
|
|
}
|
|
continue;
|
|
case 'u':
|
|
tm_info.flags |= TM_UTC;
|
|
continue;
|
|
case 'U':
|
|
unelapsed = (int)opt_info.num;
|
|
continue;
|
|
case 'z':
|
|
listzones = tm_data.zone;
|
|
continue;
|
|
case '?':
|
|
error(ERROR_usage(2), "%s", opt_info.arg);
|
|
UNREACHABLE();
|
|
case ':':
|
|
error(2, "%s", opt_info.arg);
|
|
continue;
|
|
}
|
|
break;
|
|
}
|
|
argv += opt_info.index;
|
|
if (error_info.errors)
|
|
{
|
|
error(ERROR_usage(2), "%s", optusage(NiL));
|
|
UNREACHABLE();
|
|
}
|
|
now = tmxgettime();
|
|
if (listzones)
|
|
{
|
|
s = "-";
|
|
while (listzones->standard)
|
|
{
|
|
if (listzones->type)
|
|
s = listzones->type;
|
|
sfprintf(sfstdout, "%3s %4s %4s %4d %4d\n", s, *listzones->standard ? listzones->standard : "-", listzones->daylight ? listzones->daylight : "-", listzones->west, listzones->dst);
|
|
listzones++;
|
|
show = 1;
|
|
}
|
|
}
|
|
else if (elapsed)
|
|
{
|
|
e = 0;
|
|
while (s = *argv++)
|
|
{
|
|
if (!(t = *argv++))
|
|
{
|
|
argv--;
|
|
t = "now";
|
|
}
|
|
ts = convert(fmts, s, now);
|
|
te = convert(fmts, t, now);
|
|
if (te > ts)
|
|
e += te - ts;
|
|
else
|
|
e += ts - te;
|
|
}
|
|
sfputr(sfstdout, fmtelapsed((unsigned long)tmxsec(e), 1), '\n');
|
|
show = 1;
|
|
}
|
|
else if (unelapsed)
|
|
{
|
|
while (s = *argv++)
|
|
{
|
|
u = strelapsed(s, &t, unelapsed);
|
|
if (*t)
|
|
error(3, "%s: invalid elapsed time", s);
|
|
sfprintf(sfstdout, "%lu\n", u);
|
|
}
|
|
show = 1;
|
|
}
|
|
else if (filetime)
|
|
{
|
|
if (!*argv)
|
|
{
|
|
error(ERROR_usage(2), "%s", optusage(NiL));
|
|
UNREACHABLE();
|
|
}
|
|
n = argv[1] != 0;
|
|
while (s = *argv++)
|
|
{
|
|
if (stat(s, &st))
|
|
error(2, "%s: not found", s);
|
|
else
|
|
{
|
|
switch (filetime)
|
|
{
|
|
case 'a':
|
|
now = tmxgetatime(&st);
|
|
break;
|
|
case 'c':
|
|
now = tmxgetctime(&st);
|
|
break;
|
|
default:
|
|
now = tmxgetmtime(&st);
|
|
break;
|
|
}
|
|
tmxfmt(buf, sizeof(buf), format, now);
|
|
if (n)
|
|
sfprintf(sfstdout, "%s: %s\n", s, buf);
|
|
else
|
|
sfprintf(sfstdout, "%s\n", buf);
|
|
show = 1;
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if ((s = *argv) && !format && *s == '+')
|
|
{
|
|
format = s + 1;
|
|
argv++;
|
|
s = *argv;
|
|
}
|
|
if (s || (s = string))
|
|
{
|
|
if (*argv && string)
|
|
{
|
|
error(ERROR_usage(2), "%s", optusage(NiL));
|
|
UNREACHABLE();
|
|
}
|
|
now = convert(fmts, s, now);
|
|
if (*argv && (s = *++argv))
|
|
{
|
|
show = 1;
|
|
do
|
|
{
|
|
if (!last)
|
|
{
|
|
tmxfmt(buf, sizeof(buf), format, now);
|
|
sfprintf(sfstdout, "%s\n", buf);
|
|
}
|
|
now = convert(fmts, s, now);
|
|
} while (s = *++argv);
|
|
}
|
|
}
|
|
else
|
|
show = 1;
|
|
if (format || show)
|
|
{
|
|
tmxfmt(buf, sizeof(buf), format, now);
|
|
sfprintf(sfstdout, "%s\n", buf);
|
|
}
|
|
else if (settime(context, cmd, now, increment, network))
|
|
{
|
|
error(ERROR_SYSTEM|3, "cannot set system time");
|
|
UNREACHABLE();
|
|
}
|
|
}
|
|
while (fmts != &fmt)
|
|
{
|
|
f = fmts;
|
|
fmts = fmts->next;
|
|
free(f);
|
|
}
|
|
tm_info.flags = 0;
|
|
if (show && sfsync(sfstdout))
|
|
error(ERROR_system(0), "write error");
|
|
return error_info.errors != 0;
|
|
}
|