/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- * * The contents of this file are subject to the Netscape Public License * Version 1.0 (the "NPL"); you may not use this file except in * compliance with the NPL. You may obtain a copy of the NPL at * http://www.mozilla.org/NPL/ * * Software distributed under the NPL is distributed on an "AS IS" basis, * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the NPL * for the specific language governing rights and limitations under the * NPL. * * The Initial Developer of this code under the NPL is Netscape * Communications Corporation. Portions created by Netscape are * Copyright (C) 1998 Netscape Communications Corporation. All Rights * Reserved. */ /* * * * * xp_thrmo.c --- Status message text for the thermometer. */ #include "xp.h" #include "xp_thrmo.h" #include "xpgetstr.h" #include extern int XP_THERMO_BYTE_FORMAT; extern int XP_THERMO_KBYTE_FORMAT; extern int XP_THERMO_HOURS_FORMAT; extern int XP_THERMO_MINUTES_FORMAT; extern int XP_THERMO_SECONDS_FORMAT; extern int XP_THERMO_SINGULAR_FORMAT; extern int XP_THERMO_PLURAL_FORMAT; extern int XP_THERMO_PERCENTAGE_FORMAT; extern int XP_THERMO_UH; extern int XP_THERMO_PERCENT_FORM; extern int XP_THERMO_PERCENT_RATE_FORM; extern int XP_THERMO_RAW_COUNT_FORM; extern int XP_THERMO_BYTE_RATE_FORMAT; extern int XP_THERMO_K_RATE_FORMAT; extern int XP_THERMO_M_RATE_FORMAT; extern int XP_THERMO_STALLED_FORMAT; extern int XP_THERMO_RATE_REMAINING_FORM; extern int XP_THERMO_RATE_FORM; #define KILOBYTE (1024L) #define MEGABYTE (KILOBYTE * KILOBYTE) #define MINUTE (60L) #define HOUR (MINUTE * MINUTE) #define BYTE_FORMAT XP_GetString(XP_THERMO_BYTE_FORMAT) #define KILOBYTE_FORMAT XP_GetString(XP_THERMO_KBYTE_FORMAT) #define MEGABYTE_FORMAT XP_GetString(XP_THERMO_MBYTE_FORMAT) #define BYTE_RATE_FORMAT XP_GetString(XP_THERMO_BYTE_RATE_FORMAT) #define K_RATE_FORMAT XP_GetString(XP_THERMO_K_RATE_FORMAT) #define M_RATE_FORMAT XP_GetString(XP_THERMO_M_RATE_FORMAT) #define STALLED_FORMAT XP_GetString(XP_THERMO_STALLED_FORMAT) #define HOURS_FORMAT XP_GetString(XP_THERMO_HOURS_FORMAT) #define MINUTES_FORMAT XP_GetString(XP_THERMO_MINUTES_FORMAT) #define SECONDS_FORMAT XP_GetString(XP_THERMO_SECONDS_FORMAT) #define PERCENTAGE_FORMAT XP_GetString(XP_THERMO_PERCENTAGE_FORMAT) #define UH XP_GetString(XP_THERMO_UH) #define IS_PLURAL(x) (((x) == 1) ? "" : XP_GetString(XP_THERMO_PLURAL_FORMAT) ) /* L10N? */ #define ENOUGH_TIME_TO_GUESS 5L /* #### customizable? */ #define RATE_REMAINING_FORM XP_GetString(XP_THERMO_RATE_REMAINING_FORM) #define RATE_FORM XP_GetString(XP_THERMO_RATE_FORM) #define PERCENT_FORM XP_GetString(XP_THERMO_PERCENT_FORM) #define PERCENT_RATE_FORM XP_GetString(XP_THERMO_PERCENT_RATE_FORM) #define RAW_COUNT_FORM XP_GetString(XP_THERMO_RAW_COUNT_FORM) #define STALL_TIME 4L /* Returns a text string to describe the current progress of some/all transfers in progress. total_bytes How many bytes we are waiting for, of all documents in progress. If this is unknown, it should be 0. Note that if four documents are in progress, and the sizes of three are known and the size of one is unknown, then the total_bytes should be 0, since a single unknown makes the total unknown. bytes_received How many bytes have been read so far. If total_bytes is non-0, and this is larger than total_bytes, then there is a bug. start_time_secs The time at which the transfer started, in seconds. now_secs The current time, in seconds. The 0-point of these values is uninteresting, only the relationship between The two. It would be fine for start_time_secs to always be 0, and now_secs to be the number of seconds since the transfer began. Returns: a statically allocated string, or NULL. NULL is returned in out-of-memory conditions, or when there is nothing to say (as would happen early in the transfer, if all values were 0.) DO NOT FREE THE RETURNED STRING. The caller is responsible for freeing the returned string. */ const char* XP_ProgressText( unsigned long total_bytes, unsigned long bytes_received, unsigned long start_time_secs, unsigned long now_secs ) { /* This is all fairly hairy, but generating sensible human-readable text always is. Also, this doesn't internationalize very well... */ static char* output = NULL; static char* scratch = NULL; static unsigned long last_secs = 0; static unsigned long last_bytes_received = 0; static unsigned long last_secs_left = -1; static unsigned long last_secs_received = 0; static unsigned long last_total = -1; XP_Bool size_known = total_bytes > 0; XP_Bool stalled = FALSE; unsigned long bytes_remaining = total_bytes - bytes_received; #if 0 float fmegs_received = (float)bytes_received / (float)MEGABYTE; unsigned long delta_received = bytes_received - last_bytes_received; long delta_time = now_secs - last_secs; #endif unsigned long elapsed_time = now_secs - start_time_secs; float bytes_per_sec = 0; long secs_left = -1; long how_long_since = 0; char* brief_length; char* percent; /* bytes_received / total_bytes */ char* rate; /* bytes_received / elapsed_time */ char* tleft; /* bytes_remaining / rate */ /* allocate our static buffers */ if ( !output ) { output = (char*) XP_ALLOC( 300 ); /* what if this ain't enough? */ if ( !output ) return NULL; } if ( !scratch ) { scratch = (char*) XP_ALLOC( 300 ); if ( !scratch ) { XP_FREE( output ); output = NULL; return NULL; } } /* scratch is just a buffer that we divide up for several different purposes, instead of mallocing a few small chunks. output is the buffer that we eventually write into when combining these various chunks together. */ brief_length = scratch; percent = brief_length + 20; rate = percent + 20; tleft = rate + 100; /* key off of total_bytes and update statics when it changes */ if ( total_bytes != last_total && total_bytes > 0 ) { last_secs = 0; last_bytes_received = 0; last_secs_left = -1; last_secs_received = 0; last_total = total_bytes; } if ( total_bytes < KILOBYTE ) sprintf( brief_length, BYTE_FORMAT, total_bytes ); else sprintf( brief_length, KILOBYTE_FORMAT, total_bytes / KILOBYTE ); /* boundary conditions */ if ( bytes_received <= 0 || start_time_secs <= 0 || start_time_secs >= now_secs ) { *rate = 0; *tleft = 0; } else { /* build rate and time left strings */ if ( bytes_received < last_bytes_received ) last_bytes_received = bytes_received; if ( elapsed_time > 0 ) bytes_per_sec = ( (float)bytes_received / (float)elapsed_time ); else bytes_per_sec = 0; if ( bytes_received == last_bytes_received && bytes_received != total_bytes ) { how_long_since = now_secs - last_secs_received; if ( how_long_since > STALL_TIME ) stalled = TRUE; } else last_secs_received = now_secs; /* someone is mixing the streams --- just bail. this sucks chouck 18-Sep-95 */ if ( bytes_per_sec < 0 ) return NULL; if ( elapsed_time < ENOUGH_TIME_TO_GUESS ) ; else if (bytes_per_sec != 0) { secs_left = (long)( size_known ? ( (float)bytes_remaining / bytes_per_sec ) : 0); if ( secs_left > last_secs_left ) secs_left = last_secs_left; else last_secs_left = secs_left; } /* build rate string */ if ( elapsed_time < ENOUGH_TIME_TO_GUESS ) *rate = 0; else if ( stalled ) sprintf( rate, STALLED_FORMAT ); else if ( bytes_per_sec < KILOBYTE ) sprintf( rate, BYTE_RATE_FORMAT, (long) bytes_per_sec ); else { double tmp; tmp = (double)bytes_per_sec / (double)KILOBYTE; sprintf( rate, K_RATE_FORMAT, tmp ); } /* build time left string */ if ( secs_left <= 0 || elapsed_time < ENOUGH_TIME_TO_GUESS || bytes_per_sec < KILOBYTE ) *tleft = 0; else if ( secs_left >= HOUR ) sprintf( tleft, HOURS_FORMAT, secs_left / HOUR, (secs_left / MINUTE) % MINUTE, secs_left % MINUTE ); else if ( secs_left >= MINUTE ) sprintf( tleft, MINUTES_FORMAT, secs_left / MINUTE, secs_left % MINUTE ); else sprintf( tleft, SECONDS_FORMAT, secs_left, IS_PLURAL( secs_left ) ); } /* build percentage string */ if ( size_known && total_bytes > 0 ) { int p = ( bytes_received * 100 ) / total_bytes; /* Allow it to get >100 so that we notice when netlib is lying to us, but don't treat 99.8% as 100% because people look at the 100% and read "done" instead of "almost done" and wonder what it's doing. */ if ( p >= 100 && ( bytes_received != total_bytes ) ) p = 99; sprintf( percent, PERCENTAGE_FORMAT, p ); } else { if ( bytes_received < KILOBYTE ) sprintf( percent, UH, bytes_received, IS_PLURAL( bytes_received ) ); else sprintf( percent, KILOBYTE_FORMAT, bytes_received / KILOBYTE ); } /* build output string */ if ( size_known ) { if ( total_bytes ) { if ( *tleft ) { /* "%s of %s (at %s, %s remaining)" */ sprintf( output, RATE_REMAINING_FORM, percent, brief_length, rate, tleft ); } else if ( *rate ) { /* "%s of %s (at %s)" */ sprintf( output, RATE_FORM, percent, brief_length, rate ); } else { /* "%s of %s" */ sprintf( output, PERCENT_FORM, percent, brief_length ); } } } else if ( bytes_received > 0 ) { if ( *rate ) { /* "%s read (at %s)" */ sprintf( output, PERCENT_RATE_FORM, percent, rate ); } else sprintf( output, RAW_COUNT_FORM, percent ); } else *output = 0; #if 0 XP_TRACE(( "\n" )); XP_TRACE(( "know size:%d\n", size_known )); XP_TRACE(( "total:%d rec:%d last_rec:%d\n", total_bytes, bytes_received, last_bytes_received)); XP_TRACE(( "now:%d last:%d lsr:%d\n", now_secs, last_secs, last_secs_received)); XP_TRACE(( "left:%d last_left:%d\n", secs_left, last_secs_left)); XP_TRACE(( "d:%d e:%d bps:%f\n", delta_time, elapsed_time, bytes_per_sec )); XP_TRACE(( "ti:%d\n", how_long_since )); #endif last_secs = now_secs; last_bytes_received = bytes_received; if ( *output ) { return output; } else { return NULL; } }