diff options
author | kleines Filmröllchen <filmroellchen@serenityos.org> | 2023-03-13 22:35:22 +0100 |
---|---|---|
committer | Jelle Raaijmakers <jelle@gmta.nl> | 2023-05-24 23:18:07 +0200 |
commit | effcd080ca802515ddce8392c70df512b509f57c (patch) | |
tree | a04e9a7927ddc9103129c4aa0d8b4d88976f4720 /Userland | |
parent | 82c681e44b57563074e5c12dc7e36134b36ae750 (diff) | |
download | serenity-effcd080ca802515ddce8392c70df512b509f57c.zip |
Userland: Remove remaining users of Duration::now_realtime()
This is a clear sign that they want to use a UnixDateTime instead.
This also adds support for placing durations and date times into SQL
databases via their millisecond offset to UTC.
Diffstat (limited to 'Userland')
-rw-r--r-- | Userland/Applications/Browser/CookieJar.cpp | 24 | ||||
-rw-r--r-- | Userland/Applications/Browser/StorageWidget.cpp | 2 | ||||
-rw-r--r-- | Userland/Games/BrickGame/BrickGame.cpp | 7 | ||||
-rw-r--r-- | Userland/Libraries/LibJS/Runtime/DateConstructor.cpp | 8 | ||||
-rw-r--r-- | Userland/Libraries/LibJS/Runtime/Temporal/Now.cpp | 2 | ||||
-rw-r--r-- | Userland/Libraries/LibSQL/Value.cpp | 11 | ||||
-rw-r--r-- | Userland/Libraries/LibSQL/Value.h | 3 | ||||
-rw-r--r-- | Userland/Libraries/LibWeb/Cookie/Cookie.cpp | 11 | ||||
-rw-r--r-- | Userland/Libraries/LibWeb/Cookie/Cookie.h | 6 | ||||
-rw-r--r-- | Userland/Libraries/LibWeb/Cookie/ParsedCookie.cpp | 15 | ||||
-rw-r--r-- | Userland/Libraries/LibWeb/Cookie/ParsedCookie.h | 4 | ||||
-rw-r--r-- | Userland/Libraries/LibWeb/DOM/Document.cpp | 2 | ||||
-rw-r--r-- | Userland/Libraries/LibWeb/DOM/Document.h | 2 | ||||
-rw-r--r-- | Userland/Libraries/LibWeb/FileAPI/File.cpp | 2 | ||||
-rw-r--r-- | Userland/Services/WebContent/WebDriverConnection.cpp | 6 | ||||
-rw-r--r-- | Userland/Utilities/w.cpp | 2 |
16 files changed, 62 insertions, 45 deletions
diff --git a/Userland/Applications/Browser/CookieJar.cpp b/Userland/Applications/Browser/CookieJar.cpp index 4b1abf25f1..0f2abb6c0b 100644 --- a/Userland/Applications/Browser/CookieJar.cpp +++ b/Userland/Applications/Browser/CookieJar.cpp @@ -286,7 +286,7 @@ void CookieJar::store_cookie(Web::Cookie::ParsedCookie const& parsed_cookie, con // 2. Create a new cookie with name cookie-name, value cookie-value. Set the creation-time and the last-access-time to the current date and time. Web::Cookie::Cookie cookie { parsed_cookie.name, parsed_cookie.value, parsed_cookie.same_site_attribute }; - cookie.creation_time = Duration::now_realtime(); + cookie.creation_time = UnixDateTime::now(); cookie.last_access_time = cookie.creation_time; if (parsed_cookie.expiry_time_from_max_age_attribute.has_value()) { @@ -302,7 +302,7 @@ void CookieJar::store_cookie(Web::Cookie::ParsedCookie const& parsed_cookie, con } else { // Set the cookie's persistent-flag to false. Set the cookie's expiry-time to the latest representable date. cookie.persistent = false; - cookie.expiry_time = Duration::max(); + cookie.expiry_time = UnixDateTime::latest(); } // 4. If the cookie-attribute-list contains an attribute with an attribute-name of "Domain": @@ -421,7 +421,7 @@ Vector<Web::Cookie::Cookie> CookieJar::get_matching_cookies(const URL& url, Depr }); // 3. Update the last-access-time of each cookie in the cookie-list to the current date and time. - auto now = Duration::now_realtime(); + auto now = UnixDateTime::now(); for (auto& cookie : cookie_list) { cookie.last_access_time = now; @@ -462,7 +462,7 @@ static ErrorOr<Web::Cookie::Cookie> parse_cookie(ReadonlySpan<SQL::Value> row) return Error::from_string_view(name); auto time = value.to_int<i64>().value(); - field = Duration::from_seconds(time); + field = UnixDateTime::from_seconds_since_epoch(time); return {}; }; @@ -505,9 +505,9 @@ void CookieJar::insert_cookie_into_database(Web::Cookie::Cookie const& cookie) cookie.name, cookie.value, to_underlying(cookie.same_site), - cookie.creation_time.to_seconds(), - cookie.last_access_time.to_seconds(), - cookie.expiry_time.to_seconds(), + cookie.creation_time.seconds_since_epoch(), + cookie.last_access_time.seconds_since_epoch(), + cookie.expiry_time.seconds_since_epoch(), cookie.domain, cookie.path, cookie.secure, @@ -529,9 +529,9 @@ void CookieJar::update_cookie_in_database(Web::Cookie::Cookie const& cookie) storage.statements.update_cookie, {}, [this]() { purge_expired_cookies(); }, {}, cookie.value, to_underlying(cookie.same_site), - cookie.creation_time.to_seconds(), - cookie.last_access_time.to_seconds(), - cookie.expiry_time.to_seconds(), + cookie.creation_time.seconds_since_epoch(), + cookie.last_access_time.seconds_since_epoch(), + cookie.expiry_time.seconds_since_epoch(), cookie.secure, cookie.http_only, cookie.host_only, @@ -624,7 +624,7 @@ void CookieJar::select_all_cookies_from_database(OnSelectAllCookiesResult on_res void CookieJar::purge_expired_cookies() { - auto now = Duration::now_realtime().to_seconds(); + auto now = UnixDateTime::now(); m_storage.visit( [&](PersistedStorage& storage) { @@ -634,7 +634,7 @@ void CookieJar::purge_expired_cookies() Vector<CookieStorageKey> keys_to_evict; for (auto const& cookie : storage) { - if (cookie.value.expiry_time.to_seconds() < now) + if (cookie.value.expiry_time < now) keys_to_evict.append(cookie.key); } diff --git a/Userland/Applications/Browser/StorageWidget.cpp b/Userland/Applications/Browser/StorageWidget.cpp index 9a9b20124a..624068c647 100644 --- a/Userland/Applications/Browser/StorageWidget.cpp +++ b/Userland/Applications/Browser/StorageWidget.cpp @@ -130,7 +130,7 @@ void StorageWidget::clear_session_storage_entries() void StorageWidget::delete_cookie(Web::Cookie::Cookie cookie) { // Delete cookie by making its expiry time in the past. - cookie.expiry_time = Duration::from_seconds(0); + cookie.expiry_time = UnixDateTime::earliest(); if (on_update_cookie) on_update_cookie(move(cookie)); } diff --git a/Userland/Games/BrickGame/BrickGame.cpp b/Userland/Games/BrickGame/BrickGame.cpp index b8532afa2c..7058453a5f 100644 --- a/Userland/Games/BrickGame/BrickGame.cpp +++ b/Userland/Games/BrickGame/BrickGame.cpp @@ -355,7 +355,7 @@ public: break; m_level = i; } - auto const now { Duration::now_realtime() }; + auto const now { UnixDateTime::now() }; auto const delay = s_level_map[m_level].m_delay; if (now - m_last_update > delay) { m_last_update = now; @@ -372,7 +372,7 @@ public: m_block.random_shape(); m_next_block.random_shape(); update_shadow_hint_block(); - m_last_update = Duration::now_realtime(); + m_last_update = UnixDateTime::now(); m_state = GameState::Active; } @@ -384,7 +384,8 @@ private: unsigned m_level {}; unsigned m_score {}; GameState m_state { GameState::GameOver }; - Duration m_last_update {}; + // FIXME: Should probably use a monotonic clock instead. + UnixDateTime m_last_update {}; struct LevelMap final { unsigned const m_score; diff --git a/Userland/Libraries/LibJS/Runtime/DateConstructor.cpp b/Userland/Libraries/LibJS/Runtime/DateConstructor.cpp index 0cadf2dbab..40744af009 100644 --- a/Userland/Libraries/LibJS/Runtime/DateConstructor.cpp +++ b/Userland/Libraries/LibJS/Runtime/DateConstructor.cpp @@ -131,8 +131,8 @@ static double parse_simplified_iso8601(DeprecatedString const& iso_8601) // We parsed a valid date simplified ISO 8601 string. VERIFY(year.has_value()); // A valid date string always has at least a year. - auto time = AK::Duration::from_timestamp(*year, month.value_or(1), day.value_or(1), hours.value_or(0), minutes.value_or(0), seconds.value_or(0), milliseconds.value_or(0)); - auto time_ms = static_cast<double>(time.to_milliseconds()); + auto time = AK::UnixDateTime::from_unix_time_parts(*year, month.value_or(1), day.value_or(1), hours.value_or(0), minutes.value_or(0), seconds.value_or(0), milliseconds.value_or(0)); + auto time_ms = static_cast<double>(time.milliseconds_since_epoch()); // https://tc39.es/ecma262/#sec-date.parse: // "When the UTC offset representation is absent, date-only forms are interpreted as a UTC time and date-time forms are interpreted as a local time." @@ -208,7 +208,7 @@ ThrowCompletionOr<Value> DateConstructor::call() { // 1. If NewTarget is undefined, then // a. Let now be the time value (UTC) identifying the current time. - auto now = AK::Duration::now_realtime().to_milliseconds(); + auto now = AK::UnixDateTime::now().milliseconds_since_epoch(); // b. Return ToDateString(now). return PrimitiveString::create(vm(), to_date_string(now)); @@ -225,7 +225,7 @@ ThrowCompletionOr<NonnullGCPtr<Object>> DateConstructor::construct(FunctionObjec // 3. If numberOfArgs = 0, then if (vm.argument_count() == 0) { // a. Let dv be the time value (UTC) identifying the current time. - auto now = AK::Duration::now_realtime().to_milliseconds(); + auto now = AK::UnixDateTime::now().milliseconds_since_epoch(); date_value = static_cast<double>(now); } // 4. Else if numberOfArgs = 1, then diff --git a/Userland/Libraries/LibJS/Runtime/Temporal/Now.cpp b/Userland/Libraries/LibJS/Runtime/Temporal/Now.cpp index 97d7477efb..4c81971263 100644 --- a/Userland/Libraries/LibJS/Runtime/Temporal/Now.cpp +++ b/Userland/Libraries/LibJS/Runtime/Temporal/Now.cpp @@ -165,7 +165,7 @@ TimeZone* system_time_zone(VM& vm) BigInt* system_utc_epoch_nanoseconds(VM& vm) { // 1. Let ns be the approximate current UTC date and time, in nanoseconds since the epoch. - auto now = AK::Duration::now_realtime().to_nanoseconds(); + auto now = AK::UnixDateTime::now().nanoseconds_since_epoch(); auto ns = Crypto::SignedBigInteger { now }; // 2. Set ns to the result of clamping ns between nsMinInstant and nsMaxInstant. diff --git a/Userland/Libraries/LibSQL/Value.cpp b/Userland/Libraries/LibSQL/Value.cpp index d4b495615b..6b0bb84edd 100644 --- a/Userland/Libraries/LibSQL/Value.cpp +++ b/Userland/Libraries/LibSQL/Value.cpp @@ -140,6 +140,17 @@ Value::Value(Value&& other) { } +Value::Value(Duration duration) + : m_type(SQLType::Integer) + , m_value(duration.to_milliseconds()) +{ +} + +Value::Value(UnixDateTime time) + : Value(time.offset_to_epoch()) +{ +} + Value::~Value() = default; ResultOr<Value> Value::create_tuple(NonnullRefPtr<TupleDescriptor> descriptor) diff --git a/Userland/Libraries/LibSQL/Value.h b/Userland/Libraries/LibSQL/Value.h index 1adff1382f..ad65999dcc 100644 --- a/Userland/Libraries/LibSQL/Value.h +++ b/Userland/Libraries/LibSQL/Value.h @@ -57,6 +57,9 @@ public: { } + explicit Value(UnixDateTime); + explicit Value(Duration); + static ResultOr<Value> create_tuple(NonnullRefPtr<TupleDescriptor>); static ResultOr<Value> create_tuple(Vector<Value>); diff --git a/Userland/Libraries/LibWeb/Cookie/Cookie.cpp b/Userland/Libraries/LibWeb/Cookie/Cookie.cpp index 8d5edfdf83..e24fa7b7d0 100644 --- a/Userland/Libraries/LibWeb/Cookie/Cookie.cpp +++ b/Userland/Libraries/LibWeb/Cookie/Cookie.cpp @@ -12,9 +12,10 @@ namespace Web::Cookie { -static DeprecatedString time_to_string(Duration const& time) +static DeprecatedString time_to_string(UnixDateTime const& time) { - auto local_time = Core::DateTime::from_timestamp(time.to_seconds()); + // FIXME: This roundabout formatting should not be necessary; it also loses precision. + auto local_time = Core::DateTime::from_timestamp(time.seconds_since_epoch()); return local_time.to_deprecated_string("%Y-%m-%d %H:%M:%S %Z"sv); } @@ -87,11 +88,11 @@ ErrorOr<Web::Cookie::Cookie> IPC::decode(Decoder& decoder) auto value = TRY(decoder.decode<DeprecatedString>()); auto domain = TRY(decoder.decode<DeprecatedString>()); auto path = TRY(decoder.decode<DeprecatedString>()); - auto creation_time = TRY(decoder.decode<Duration>()); - auto expiry_time = TRY(decoder.decode<Duration>()); + auto creation_time = TRY(decoder.decode<UnixDateTime>()); + auto expiry_time = TRY(decoder.decode<UnixDateTime>()); auto host_only = TRY(decoder.decode<bool>()); auto http_only = TRY(decoder.decode<bool>()); - auto last_access_time = TRY(decoder.decode<Duration>()); + auto last_access_time = TRY(decoder.decode<UnixDateTime>()); auto persistent = TRY(decoder.decode<bool>()); auto secure = TRY(decoder.decode<bool>()); auto same_site = TRY(decoder.decode<Web::Cookie::SameSite>()); diff --git a/Userland/Libraries/LibWeb/Cookie/Cookie.h b/Userland/Libraries/LibWeb/Cookie/Cookie.h index 805dadb20c..34a34aba81 100644 --- a/Userland/Libraries/LibWeb/Cookie/Cookie.h +++ b/Userland/Libraries/LibWeb/Cookie/Cookie.h @@ -32,9 +32,9 @@ struct Cookie { DeprecatedString name; DeprecatedString value; SameSite same_site; - Duration creation_time {}; - Duration last_access_time {}; - Duration expiry_time {}; + UnixDateTime creation_time {}; + UnixDateTime last_access_time {}; + UnixDateTime expiry_time {}; DeprecatedString domain {}; DeprecatedString path {}; bool secure { false }; diff --git a/Userland/Libraries/LibWeb/Cookie/ParsedCookie.cpp b/Userland/Libraries/LibWeb/Cookie/ParsedCookie.cpp index 469106c02a..79b4968199 100644 --- a/Userland/Libraries/LibWeb/Cookie/ParsedCookie.cpp +++ b/Userland/Libraries/LibWeb/Cookie/ParsedCookie.cpp @@ -27,7 +27,7 @@ static void on_path_attribute(ParsedCookie& parsed_cookie, StringView attribute_ static void on_secure_attribute(ParsedCookie& parsed_cookie); static void on_http_only_attribute(ParsedCookie& parsed_cookie); static void on_same_site_attribute(ParsedCookie& parsed_cookie, StringView attribute_value); -static Optional<Duration> parse_date_time(StringView date_string); +static Optional<UnixDateTime> parse_date_time(StringView date_string); Optional<ParsedCookie> parse_cookie(DeprecatedString const& cookie_string) { @@ -169,10 +169,10 @@ void on_max_age_attribute(ParsedCookie& parsed_cookie, StringView attribute_valu if (auto delta_seconds = attribute_value.to_int(); delta_seconds.has_value()) { if (*delta_seconds <= 0) { // If delta-seconds is less than or equal to zero (0), let expiry-time be the earliest representable date and time. - parsed_cookie.expiry_time_from_max_age_attribute = Duration::min(); + parsed_cookie.expiry_time_from_max_age_attribute = UnixDateTime::earliest(); } else { // Otherwise, let the expiry-time be the current date and time plus delta-seconds seconds. - parsed_cookie.expiry_time_from_max_age_attribute = Duration::now_realtime() + Duration::from_seconds(*delta_seconds); + parsed_cookie.expiry_time_from_max_age_attribute = UnixDateTime::now() + Duration::from_seconds(*delta_seconds); } } } @@ -236,7 +236,7 @@ void on_same_site_attribute(ParsedCookie& parsed_cookie, StringView attribute_va parsed_cookie.same_site_attribute = same_site_from_string(attribute_value); } -Optional<Duration> parse_date_time(StringView date_string) +Optional<UnixDateTime> parse_date_time(StringView date_string) { // https://tools.ietf.org/html/rfc6265#section-5.1.1 unsigned hour = 0; @@ -345,7 +345,8 @@ Optional<Duration> parse_date_time(StringView date_string) // day-of-month-value, the month-value, the year-value, the hour-value, the minute-value, and the second-value, respectively. // If no such date exists, abort these steps and fail to parse the cookie-date. // FIXME: Fail on dates that do not exist. - auto parsed_cookie_date = Duration::from_timestamp(year, month, day_of_month, hour, minute, second, 0); + // FIXME: This currently uses UNIX time, which is not equivalent to UTC due to leap seconds. + auto parsed_cookie_date = UnixDateTime::from_unix_time_parts(year, month, day_of_month, hour, minute, second, 0); // 7. Return the parsed-cookie-date as the result of this algorithm. return parsed_cookie_date; @@ -374,8 +375,8 @@ ErrorOr<Web::Cookie::ParsedCookie> IPC::decode(Decoder& decoder) { auto name = TRY(decoder.decode<DeprecatedString>()); auto value = TRY(decoder.decode<DeprecatedString>()); - auto expiry_time_from_expires_attribute = TRY(decoder.decode<Optional<Duration>>()); - auto expiry_time_from_max_age_attribute = TRY(decoder.decode<Optional<Duration>>()); + auto expiry_time_from_expires_attribute = TRY(decoder.decode<Optional<UnixDateTime>>()); + auto expiry_time_from_max_age_attribute = TRY(decoder.decode<Optional<UnixDateTime>>()); auto domain = TRY(decoder.decode<Optional<DeprecatedString>>()); auto path = TRY(decoder.decode<Optional<DeprecatedString>>()); auto secure_attribute_present = TRY(decoder.decode<bool>()); diff --git a/Userland/Libraries/LibWeb/Cookie/ParsedCookie.h b/Userland/Libraries/LibWeb/Cookie/ParsedCookie.h index 339716639b..1d47121daa 100644 --- a/Userland/Libraries/LibWeb/Cookie/ParsedCookie.h +++ b/Userland/Libraries/LibWeb/Cookie/ParsedCookie.h @@ -18,8 +18,8 @@ struct ParsedCookie { DeprecatedString name; DeprecatedString value; SameSite same_site_attribute { SameSite::Default }; - Optional<Duration> expiry_time_from_expires_attribute {}; - Optional<Duration> expiry_time_from_max_age_attribute {}; + Optional<UnixDateTime> expiry_time_from_expires_attribute {}; + Optional<UnixDateTime> expiry_time_from_max_age_attribute {}; Optional<DeprecatedString> domain {}; Optional<DeprecatedString> path {}; bool secure_attribute_present { false }; diff --git a/Userland/Libraries/LibWeb/DOM/Document.cpp b/Userland/Libraries/LibWeb/DOM/Document.cpp index 0458bd506a..321525d278 100644 --- a/Userland/Libraries/LibWeb/DOM/Document.cpp +++ b/Userland/Libraries/LibWeb/DOM/Document.cpp @@ -1651,7 +1651,7 @@ void Document::completely_finish_loading() VERIFY(browsing_context()); // 2. Set document's completely loaded time to the current time. - m_completely_loaded_time = AK::Duration::now_realtime(); + m_completely_loaded_time = AK::UnixDateTime::now(); // 3. Let container be document's browsing context's container. auto container = JS::make_handle(browsing_context()->container()); diff --git a/Userland/Libraries/LibWeb/DOM/Document.h b/Userland/Libraries/LibWeb/DOM/Document.h index 7b85780da7..86aa2c6ba3 100644 --- a/Userland/Libraries/LibWeb/DOM/Document.h +++ b/Userland/Libraries/LibWeb/DOM/Document.h @@ -612,7 +612,7 @@ private: JS::GCPtr<HTMLCollection> m_all; // https://html.spec.whatwg.org/#completely-loaded-time - Optional<AK::Duration> m_completely_loaded_time; + Optional<AK::UnixDateTime> m_completely_loaded_time; // https://html.spec.whatwg.org/multipage/dom.html#concept-document-navigation-id Optional<String> m_navigation_id; diff --git a/Userland/Libraries/LibWeb/FileAPI/File.cpp b/Userland/Libraries/LibWeb/FileAPI/File.cpp index 87a5df33bc..cc11fdd258 100644 --- a/Userland/Libraries/LibWeb/FileAPI/File.cpp +++ b/Userland/Libraries/LibWeb/FileAPI/File.cpp @@ -59,7 +59,7 @@ WebIDL::ExceptionOr<JS::NonnullGCPtr<File>> File::create(JS::Realm& realm, Vecto // 3. If the lastModified member is provided, let d be set to the lastModified dictionary member. If it is not provided, set d to the current date and time represented as the number of milliseconds since the Unix Epoch (which is the equivalent of Date.now() [ECMA-262]). // Note: Since ECMA-262 Date objects convert to long long values representing the number of milliseconds since the Unix Epoch, the lastModified member could be a Date object [ECMA-262]. - last_modified = options->last_modified.has_value() ? options->last_modified.value() : Duration::now_realtime().to_milliseconds(); + last_modified = options->last_modified.has_value() ? options->last_modified.value() : UnixDateTime::now().milliseconds_since_epoch(); } // 4. Return a new File object F such that: diff --git a/Userland/Services/WebContent/WebDriverConnection.cpp b/Userland/Services/WebContent/WebDriverConnection.cpp index c6c64fee1e..0121ac5f96 100644 --- a/Userland/Services/WebContent/WebDriverConnection.cpp +++ b/Userland/Services/WebContent/WebDriverConnection.cpp @@ -56,7 +56,7 @@ static JsonValue serialize_cookie(Web::Cookie::Cookie const& cookie) serialized_cookie.set("domain"sv, cookie.domain); serialized_cookie.set("secure"sv, cookie.secure); serialized_cookie.set("httpOnly"sv, cookie.http_only); - serialized_cookie.set("expiry"sv, cookie.expiry_time.to_seconds()); + serialized_cookie.set("expiry"sv, cookie.expiry_time.seconds_since_epoch()); serialized_cookie.set("sameSite"sv, Web::Cookie::same_site_to_string(cookie.same_site)); return serialized_cookie; @@ -1602,7 +1602,7 @@ Messages::WebDriverClient::AddCookieResponse WebDriverConnection::add_cookie(Jso if (data.has("expiry"sv)) { // NOTE: less than 0 or greater than safe integer are handled by the JSON parser auto expiry = TRY(get_property<u32>(data, "expiry"sv)); - cookie.expiry_time_from_expires_attribute = Duration::from_seconds(expiry); + cookie.expiry_time_from_expires_attribute = UnixDateTime::from_seconds_since_epoch(expiry); } // Cookie same site @@ -2039,7 +2039,7 @@ void WebDriverConnection::delete_cookies(Optional<StringView> const& name) // -> name is equal to cookie name if (!name.has_value() || name.value() == cookie.name) { // Set the cookie expiry time to a Unix timestamp in the past. - cookie.expiry_time = Duration::from_seconds(0); + cookie.expiry_time = UnixDateTime::earliest(); m_page_client.page_did_update_cookie(move(cookie)); } // -> Otherwise diff --git a/Userland/Utilities/w.cpp b/Userland/Utilities/w.cpp index bab3681a3a..fbb0dbdebe 100644 --- a/Userland/Utilities/w.cpp +++ b/Userland/Utilities/w.cpp @@ -63,7 +63,7 @@ ErrorOr<int> serenity_main(Main::Arguments args) auto process_statistics = TRY(Core::ProcessStatisticsReader::get_all()); - auto now = Time::now_realtime().to_seconds(); + auto now = UnixDateTime::now().seconds_since_epoch(); if (!hide_header) outln("\033[1m{:10} {:12} {:16} {:6} {}\033[0m", "USER", "TTY", "LOGIN@", "IDLE", "WHAT"); |