| File: | battstat/apmlib/apmlib.c |
| Warning: | line 142, column 11 Read function called when stream is in EOF state. Function has no effect |
Press '?' to see keyboard shortcuts
Keyboard shortcuts:
| 1 | /* apmlib.c -- Sample APM interface routines | |||
| 2 | * Created: Mon Jan 8 10:28:16 1996 by faith@acm.org | |||
| 3 | * Revised: Fri Dec 26 21:38:29 1997 by faith@acm.org | |||
| 4 | * Copyright 1996, 1997 Rickard E. Faith (faith@acm.org) | |||
| 5 | * | |||
| 6 | * This library is free software; you can redistribute it and/or modify it | |||
| 7 | * under the terms of the GNU Library General Public License as published | |||
| 8 | * by the Free Software Foundation; either version 2 of the License, or (at | |||
| 9 | * your option) any later version. | |||
| 10 | * | |||
| 11 | * This library is distributed in the hope that it will be useful, but | |||
| 12 | * WITHOUT ANY WARRANTY; without even the implied warranty of | |||
| 13 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU | |||
| 14 | * Library General Public License for more details. | |||
| 15 | * | |||
| 16 | * You should have received a copy of the GNU Library General Public | |||
| 17 | * License along with this library; if not, write to the Free Software | |||
| 18 | * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA. | |||
| 19 | * | |||
| 20 | */ | |||
| 21 | #include <stdio.h> | |||
| 22 | #include <stdlib.h> | |||
| 23 | #include <unistd.h> | |||
| 24 | #include <string.h> | |||
| 25 | #include <fcntl.h> | |||
| 26 | #include <ctype.h> | |||
| 27 | #include <sys/stat.h> | |||
| 28 | #include <sys/time.h> | |||
| 29 | #include <sys/ioctl.h> | |||
| 30 | #include <sys/types.h> | |||
| 31 | #include <sys/sysmacros.h> | |||
| 32 | #include "apm.h" | |||
| 33 | ||||
| 34 | #define BACKWARD_COMPAT1 1 | |||
| 35 | ||||
| 36 | /* If APM support of the right version exists in kernel, return zero. | |||
| 37 | * Otherwise, return 1 if no support exists, or 2 if it is the wrong | |||
| 38 | * version. *NOTE* The sense of the return value is not intuitive. | |||
| 39 | */ | |||
| 40 | int apm_exists(void) | |||
| 41 | { | |||
| 42 | apm_info i; | |||
| 43 | ||||
| 44 | if (access(APM_PROC"/proc/apm", R_OK4)) | |||
| 45 | return 1; | |||
| 46 | return apm_read(&i); | |||
| 47 | } | |||
| 48 | ||||
| 49 | ||||
| 50 | /* Read information from /proc/apm. Return 0 on success, 1 if APM not | |||
| 51 | * installed, 2 if APM installed, but old version. | |||
| 52 | */ | |||
| 53 | int apm_read(apm_info * i) | |||
| 54 | { | |||
| 55 | FILE *str; | |||
| 56 | char units[10]; | |||
| 57 | char buffer[100]; | |||
| 58 | int retcode = 0; | |||
| 59 | ||||
| 60 | if (!(str = fopen(APM_PROC"/proc/apm", "r"))) | |||
| 61 | return 1; | |||
| 62 | ||||
| 63 | if (fgets(buffer, sizeof(buffer) - 1, str) == NULL((void*)0)) | |||
| 64 | printf("fgets error\n"); | |||
| 65 | ||||
| 66 | buffer[sizeof(buffer) - 1] = '\0'; | |||
| 67 | ||||
| 68 | /* Should check for other driver versions; driver 1.9 (and some | |||
| 69 | * others) uses this format, which doesn't expose # batteries. | |||
| 70 | */ | |||
| 71 | sscanf(buffer, "%s %d.%d %x %x %x %x %d%% %d %s\n", | |||
| 72 | (char *) i->driver_version, | |||
| 73 | &i->apm_version_major, | |||
| 74 | &i->apm_version_minor, | |||
| 75 | &i->apm_flags, | |||
| 76 | &i->ac_line_status, | |||
| 77 | &i->battery_status, | |||
| 78 | &i->battery_flags, | |||
| 79 | &i->battery_percentage, | |||
| 80 | &i->battery_time, | |||
| 81 | units); | |||
| 82 | i->using_minutes = !strncmp(units, "min", 3) ? 1 : 0; | |||
| 83 | if (i->driver_version[0] == 'B') | |||
| 84 | { /* old style. argh. */ | |||
| 85 | #if !BACKWARD_COMPAT1 | |||
| 86 | retcode = 2; | |||
| 87 | #else | |||
| 88 | strcpy((char *) i->driver_version, "pre-0.7"); | |||
| 89 | i->apm_version_major = 0; | |||
| 90 | i->apm_version_minor = 0; | |||
| 91 | i->apm_flags = 0; | |||
| 92 | i->ac_line_status = 0xff; | |||
| 93 | i->battery_status = 0xff; | |||
| 94 | i->battery_flags = 0xff; | |||
| 95 | i->battery_percentage = -1; | |||
| 96 | i->battery_time = -1; | |||
| 97 | i->using_minutes = 1; | |||
| 98 | ||||
| 99 | sscanf(buffer, "BIOS version: %d.%d", | |||
| 100 | &i->apm_version_major, &i->apm_version_minor); | |||
| 101 | ||||
| 102 | if (fgets(buffer, sizeof(buffer) - 1, str) == NULL((void*)0)) | |||
| 103 | printf("fgets error\n"); | |||
| 104 | ||||
| 105 | sscanf(buffer, "Flags: 0x%02x", &i->apm_flags); | |||
| 106 | if (i->apm_flags & APM_32_BIT_SUPPORT0x0002) | |||
| 107 | { | |||
| 108 | if (fgets(buffer, sizeof(buffer) - 1, str) == NULL((void*)0)) | |||
| 109 | printf("fgets error\n"); | |||
| 110 | ||||
| 111 | if (fgets(buffer, sizeof(buffer) - 1, str) == NULL((void*)0)) | |||
| 112 | printf("fgets error\n"); | |||
| 113 | ||||
| 114 | if (buffer[0] != 'P') | |||
| 115 | { | |||
| 116 | if (!strncmp(buffer + 4, "off line", 8)) | |||
| 117 | i->ac_line_status = 0; | |||
| 118 | else if (!strncmp(buffer + 4, "on line", 7)) | |||
| 119 | i->ac_line_status = 1; | |||
| 120 | else if (!strncmp(buffer + 4, "on back", 7)) | |||
| 121 | i->ac_line_status = 2; | |||
| 122 | ||||
| 123 | if (fgets(buffer, sizeof(buffer) - 1, str) == NULL((void*)0)) | |||
| 124 | printf("fgets error\n"); | |||
| 125 | ||||
| 126 | if (!strncmp(buffer + 16, "high", 4)) | |||
| 127 | i->battery_status = 0; | |||
| 128 | else if (!strncmp(buffer + 16, "low", 3)) | |||
| 129 | i->battery_status = 1; | |||
| 130 | else if (!strncmp(buffer + 16, "crit", 4)) | |||
| 131 | i->battery_status = 2; | |||
| 132 | else if (!strncmp(buffer + 16, "charg", 5)) | |||
| 133 | i->battery_status = 3; | |||
| 134 | ||||
| 135 | if (fgets(buffer, sizeof(buffer) - 1, str) == NULL((void*)0)) | |||
| 136 | printf("fgets error\n"); | |||
| 137 | ||||
| 138 | if (strncmp(buffer + 14, "unknown", 7)) | |||
| 139 | i->battery_percentage = atoi(buffer + 14); | |||
| 140 | if (i->apm_version_major >= 1 && i->apm_version_minor >= 1) | |||
| 141 | { | |||
| 142 | if (fgets(buffer, sizeof(buffer) - 1, str) == NULL((void*)0)) | |||
| ||||
| 143 | printf("fgets error\n"); | |||
| 144 | ||||
| 145 | sscanf(buffer, "Battery flag: 0x%02x", &i->battery_flags); | |||
| 146 | ||||
| 147 | if (fgets(buffer, sizeof(buffer) - 1, str) == NULL((void*)0)) | |||
| 148 | printf("fgets error\n"); | |||
| 149 | ||||
| 150 | if (strncmp(buffer + 14, "unknown", 7)) | |||
| 151 | i->battery_time = atoi(buffer + 14); | |||
| 152 | } | |||
| 153 | } | |||
| 154 | } | |||
| 155 | #endif | |||
| 156 | } | |||
| 157 | ||||
| 158 | /* Fix possible kernel bug -- percentage | |||
| 159 | * set to 0xff (==255) instead of -1. | |||
| 160 | */ | |||
| 161 | if (i->battery_percentage > 100) | |||
| 162 | i->battery_percentage = -1; | |||
| 163 | ||||
| 164 | fclose(str); | |||
| 165 | return retcode; | |||
| 166 | } | |||
| 167 | ||||
| 168 | ||||
| 169 | /* Lookup the device number for the apm_bios device. */ | |||
| 170 | dev_t apm_dev(void) | |||
| 171 | { | |||
| 172 | FILE *str; | |||
| 173 | static int cached = -1; | |||
| 174 | char buf[80]; | |||
| 175 | char *pt; | |||
| 176 | apm_info i; | |||
| 177 | ||||
| 178 | if (cached >= 0) | |||
| 179 | return cached; | |||
| 180 | ||||
| 181 | if (access(APM_PROC"/proc/apm", R_OK4) || apm_read(&i) == 1) | |||
| 182 | return cached = -1; | |||
| 183 | if (i.driver_version[0] == '1') | |||
| 184 | return cached = makedev(10, 134)gnu_dev_makedev (10, 134); | |||
| 185 | ||||
| 186 | if (!(str = fopen(APM_DEV"/proc/devices", "r"))) | |||
| 187 | return -1; | |||
| 188 | while (fgets(buf, sizeof(buf) - 1, str)) | |||
| 189 | { | |||
| 190 | buf[sizeof(buf) - 1] = '\0'; | |||
| 191 | for (pt = buf; *pt && isspace(*pt)((*__ctype_b_loc ())[(int) ((*pt))] & (unsigned short int ) _ISspace); ++pt); /* skip leading spaces */ | |||
| 192 | for (; *pt && !isspace(*pt)((*__ctype_b_loc ())[(int) ((*pt))] & (unsigned short int ) _ISspace); ++pt); /* find next space */ | |||
| 193 | if (isspace(*pt)((*__ctype_b_loc ())[(int) ((*pt))] & (unsigned short int ) _ISspace)) | |||
| 194 | { | |||
| 195 | *pt++ = '\0'; | |||
| 196 | pt[strlen(pt) - 1] = '\0'; /* get rid of newline */ | |||
| 197 | if (!strcmp(pt, APM_NAME"apm_bios")) | |||
| 198 | { | |||
| 199 | fclose(str); | |||
| 200 | return cached = makedev(atoi(buf), 0)gnu_dev_makedev (atoi(buf), 0); | |||
| 201 | } | |||
| 202 | } | |||
| 203 | } | |||
| 204 | fclose(str); | |||
| 205 | return cached = -1; | |||
| 206 | } | |||
| 207 | ||||
| 208 | ||||
| 209 | /* Return a file descriptor for the apm_bios device, or -1 if there is an | |||
| 210 | * error. Is this method secure? Should we make the device in /dev | |||
| 211 | * instead of /tmp? | |||
| 212 | * | |||
| 213 | * apenwarr 2001/05/11: just throw out the weird temporary device file stuff. | |||
| 214 | * It was only for ancient kernel versions anyway. | |||
| 215 | */ | |||
| 216 | int apm_open(void) | |||
| 217 | { | |||
| 218 | int fd; | |||
| 219 | apm_info i; | |||
| 220 | ||||
| 221 | if (access(APM_PROC"/proc/apm", R_OK4) || apm_read(&i) == 1) | |||
| ||||
| 222 | return -1; | |||
| 223 | if (i.driver_version[0] >= '1') | |||
| 224 | { | |||
| 225 | if ((fd = open(APM_DEVICE"/dev/apm_bios", O_RDWR02)) < 0) | |||
| 226 | { | |||
| 227 | /* Try to create it. This is reasonable | |||
| 228 | * for backward compatibility. | |||
| 229 | */ | |||
| 230 | if (mknod(APM_DEVICE"/dev/apm_bios", S_IFCHR0020000 | S_IRUSR0400 | S_IWUSR0200, apm_dev())) | |||
| 231 | { | |||
| 232 | unlink(APM_DEVICE"/dev/apm_bios"); | |||
| 233 | return -1; | |||
| 234 | } | |||
| 235 | fd = open(APM_DEVICE"/dev/apm_bios", O_RDWR02); | |||
| 236 | } | |||
| 237 | ||||
| 238 | return fd; | |||
| 239 | } | |||
| 240 | ||||
| 241 | return -1; | |||
| 242 | } | |||
| 243 | ||||
| 244 | ||||
| 245 | /* Given a file descriptor for the apm_bios device, close it. */ | |||
| 246 | int apm_close(int fd) | |||
| 247 | { | |||
| 248 | return close(fd); | |||
| 249 | } | |||
| 250 | ||||
| 251 | /* Given a file descriptor for the apm_bios device, this routine will wait | |||
| 252 | * timeout seconds for APM events. Up to n events will be placed in the | |||
| 253 | * events queue. The return code will indicate the number of events | |||
| 254 | * stored. Since this routine uses select(2), it will return if an | |||
| 255 | * unblocked signal is caught. A timeout < 0 means to block indefinately. | |||
| 256 | * | |||
| 257 | * Note that if you read a request to standby or to suspend, the kernel | |||
| 258 | * will be waiting for you to respond to it with a call to apm_suspend() | |||
| 259 | * or to apm_standby() ! | |||
| 260 | */ | |||
| 261 | int apm_get_events(int fd, int timeout, apm_event_t * events, int n) | |||
| 262 | { | |||
| 263 | int retcode; | |||
| 264 | fd_set fds; | |||
| 265 | struct timeval t; | |||
| 266 | ||||
| 267 | t.tv_sec = timeout; | |||
| 268 | t.tv_usec = 0; | |||
| 269 | ||||
| 270 | FD_ZERO(&fds)do { unsigned int __i; fd_set *__arr = (&fds); for (__i = 0; __i < sizeof (fd_set) / sizeof (__fd_mask); ++__i) ((__arr )->__fds_bits)[__i] = 0; } while (0); | |||
| 271 | FD_SET(fd, &fds)((void) (((&fds)->__fds_bits)[((fd) / (8 * (int) sizeof (__fd_mask)))] |= ((__fd_mask) (1UL << ((fd) % (8 * (int ) sizeof (__fd_mask))))))); | |||
| 272 | retcode = select(fd + 1, &fds, NULL((void*)0), NULL((void*)0), timeout < 0 ? NULL((void*)0) : &t); | |||
| 273 | if (retcode <= 0) | |||
| 274 | return 0; | |||
| 275 | return read(fd, events, n * sizeof(apm_event_t)) / sizeof(apm_event_t); | |||
| 276 | } | |||
| 277 | ||||
| 278 | ||||
| 279 | /* Try to set the Power State to Suspend. */ | |||
| 280 | int apm_suspend(int fd) | |||
| 281 | { | |||
| 282 | sync(); | |||
| 283 | return ioctl(fd, APM_IOC_SUSPEND(((0U) << (((0 +8)+8)+14)) | ((('A')) << (0 +8)) | (((2)) << 0) | ((0) << ((0 +8)+8))), NULL((void*)0)); | |||
| 284 | } | |||
| 285 | ||||
| 286 | ||||
| 287 | /* Try to set the Power State to Standby. */ | |||
| 288 | int apm_standby(int fd) | |||
| 289 | { | |||
| 290 | sync(); | |||
| 291 | return ioctl(fd, APM_IOC_STANDBY(((0U) << (((0 +8)+8)+14)) | ((('A')) << (0 +8)) | (((1)) << 0) | ((0) << ((0 +8)+8))), NULL((void*)0)); | |||
| 292 | } | |||
| 293 | ||||
| 294 | /* Return the last error code generated by the kernel APM driver */ | |||
| 295 | unsigned int apm_last_error( int fd ) | |||
| 296 | { | |||
| 297 | int err = 0; | |||
| 298 | ||||
| 299 | #ifdef APM_IOC_LAST_ERROR | |||
| 300 | int ierr = 0; | |||
| 301 | if ( (ierr = ioctl( fd, APM_IOC_LAST_ERROR, &err)) ) | |||
| 302 | return ierr; | |||
| 303 | #endif | |||
| 304 | return err; | |||
| 305 | } | |||
| 306 | ||||
| 307 | /* Define lookup table for error messages */ | |||
| 308 | typedef struct lookup_t { | |||
| 309 | int key; | |||
| 310 | char * msg; | |||
| 311 | } lookup_t; | |||
| 312 | ||||
| 313 | /* APM error messages, arranged by error code */ | |||
| 314 | static const lookup_t error_table[] = { | |||
| 315 | /* N/A { APM_SUCCESS, "Operation succeeded" }, */ | |||
| 316 | { APM_DISABLED0x01, "Power management disabled" }, | |||
| 317 | { APM_CONNECTED0x02, "Real mode interface already connected" }, | |||
| 318 | { APM_NOT_CONNECTED0x03, "Interface not connected" }, | |||
| 319 | { APM_16_CONNECTED0x05, "16 bit interface already connected" }, | |||
| 320 | /* N/A { APM_16_UNSUPPORTED, "16 bit interface not supported" }, */ | |||
| 321 | { APM_32_CONNECTED0x07, "32 bit interface already connected" }, | |||
| 322 | { APM_32_UNSUPPORTED0x08, "32 bit interface not supported" }, | |||
| 323 | { APM_BAD_DEVICE0x09, "Unrecognized device ID" }, | |||
| 324 | { APM_BAD_PARAM0x0a, "Parameter out of range" }, | |||
| 325 | { APM_NOT_ENGAGED0x0b, "Interface not engaged" }, | |||
| 326 | #ifdef APM_BAD_FUNCTION0x0c | |||
| 327 | { APM_BAD_FUNCTION0x0c, "Function not supported" }, | |||
| 328 | #endif | |||
| 329 | #ifdef APM_RESUME_DISABLED0x0d | |||
| 330 | { APM_RESUME_DISABLED0x0d, "Resume timer disabled" }, | |||
| 331 | #endif | |||
| 332 | { APM_BAD_STATE0x60, "Unable to enter requested state" }, | |||
| 333 | /* N/A { APM_NO_EVENTS, "No events pending" }, */ | |||
| 334 | { APM_NOT_PRESENT0x86, "No APM present" } | |||
| 335 | }; | |||
| 336 | #define ERROR_COUNT(sizeof(error_table)/sizeof(lookup_t)) (sizeof(error_table)/sizeof(lookup_t)) | |||
| 337 | ||||
| 338 | /* Return character string describing error messages from APM kernel */ | |||
| 339 | const char *apm_error_name( unsigned int err ) | |||
| 340 | { | |||
| 341 | int i; | |||
| 342 | ||||
| 343 | for(i=0; i<ERROR_COUNT(sizeof(error_table)/sizeof(lookup_t)); i++) | |||
| 344 | if(err == error_table[i].key) return(error_table[i].msg); | |||
| 345 | ||||
| 346 | return "Unknown error"; | |||
| 347 | } | |||
| 348 | ||||
| 349 | int apm_reject( int fd ) | |||
| 350 | { | |||
| 351 | #ifdef APM_IOC_REJECT | |||
| 352 | if ( ioctl( fd, APM_IOC_REJECT, NULL((void*)0) ) ) | |||
| 353 | return apm_last_error( fd ); | |||
| 354 | else | |||
| 355 | #endif | |||
| 356 | return 0; | |||
| 357 | } | |||
| 358 | ||||
| 359 | #ifdef APM_IOC_IGNORE /* detect kernel support of IGNORE/NOIGNORE functions */ | |||
| 360 | int apm_set_ignore(int fd, int mode) | |||
| 361 | /* Ignore Standby. */ | |||
| 362 | { | |||
| 363 | if (mode == IGNORE2) | |||
| 364 | { | |||
| 365 | printf("Telling kernel to ignore system standby/suspend mode\n"); | |||
| 366 | return ioctl(fd, APM_IOC_IGNORE, NULL((void*)0)); | |||
| 367 | } | |||
| 368 | else | |||
| 369 | { | |||
| 370 | printf("Telling kernel not to ignore system standby/suspend mode\n"); | |||
| 371 | return ioctl(fd, APM_IOC_NOIGNORE, NULL((void*)0)); | |||
| 372 | } | |||
| 373 | printf("NOTE: User-generated suspend/standby requests are not ignored\n"); | |||
| 374 | } | |||
| 375 | #endif | |||
| 376 | ||||
| 377 | /* Return a string describing the event. From p. 16 of the Intel/Microsoft | |||
| 378 | * Advanded Power Management (APM) BIOS Interface Specification, Revision | |||
| 379 | * 1.1 (September 1993). Intel Order Number: 241704-001. Microsoft Part | |||
| 380 | * Number: 781-110-X01. | |||
| 381 | * | |||
| 382 | * Updated to APM BIOS 1.2 spec (February 1996). Available on-line. | |||
| 383 | */ | |||
| 384 | const char *apm_event_name(apm_event_t event) | |||
| 385 | { | |||
| 386 | switch (event) | |||
| 387 | { | |||
| 388 | case APM_SYS_STANDBY0x0001: | |||
| 389 | return "System Standby Request"; | |||
| 390 | case APM_SYS_SUSPEND0x0002: | |||
| 391 | return "System Suspend Request"; | |||
| 392 | case APM_NORMAL_RESUME0x0003: | |||
| 393 | return "Normal Resume System"; | |||
| 394 | case APM_CRITICAL_RESUME0x0004: | |||
| 395 | return "Critical Resume System"; | |||
| 396 | case APM_LOW_BATTERY0x0005: | |||
| 397 | return "Battery Low"; | |||
| 398 | case APM_POWER_STATUS_CHANGE0x0006: | |||
| 399 | return "Power Status Change"; | |||
| 400 | case APM_UPDATE_TIME0x0007: | |||
| 401 | return "Update Time"; | |||
| 402 | case APM_CRITICAL_SUSPEND0x0008: | |||
| 403 | return "Critical Suspend"; | |||
| 404 | case APM_USER_STANDBY0x0009: | |||
| 405 | return "User System Standby Request"; | |||
| 406 | case APM_USER_SUSPEND0x000a: | |||
| 407 | return "User System Suspend Request"; | |||
| 408 | case APM_STANDBY_RESUME0x000b: | |||
| 409 | return "System Standby Resume"; | |||
| 410 | #ifdef APM_CAPABILITY_CHANGE0x000c | |||
| 411 | case APM_CAPABILITY_CHANGE0x000c: | |||
| 412 | return "Capability Change"; | |||
| 413 | #endif | |||
| 414 | } | |||
| 415 | return "Unknown"; | |||
| 416 | } | |||
| 417 | ||||
| 418 | ||||
| 419 | /* This is a convenience function that has nothing to do with APM. It just | |||
| 420 | * formats a time nicely. If you don't like this format, then write your | |||
| 421 | * own. | |||
| 422 | */ | |||
| 423 | #define SEC_PER_DAY(60*60*24) (60*60*24) | |||
| 424 | #define SEC_PER_HOUR(60*60) (60*60) | |||
| 425 | #define SEC_PER_MIN(60) (60) | |||
| 426 | ||||
| 427 | const char *apm_delta_time(time_t then, time_t now) | |||
| 428 | { | |||
| 429 | return apm_time(now - then); | |||
| 430 | } | |||
| 431 | ||||
| 432 | const char *apm_time(time_t t) | |||
| 433 | { | |||
| 434 | static char buffer[128]; | |||
| 435 | unsigned long s, m, h, d; | |||
| 436 | ||||
| 437 | d = t / SEC_PER_DAY(60*60*24); | |||
| 438 | t -= d * SEC_PER_DAY(60*60*24); | |||
| 439 | h = t / SEC_PER_HOUR(60*60); | |||
| 440 | t -= h * SEC_PER_HOUR(60*60); | |||
| 441 | m = t / SEC_PER_MIN(60); | |||
| 442 | t -= m * SEC_PER_MIN(60); | |||
| 443 | s = t; | |||
| 444 | ||||
| 445 | if (d) | |||
| 446 | sprintf(buffer, "%lu day%s, %02lu:%02lu:%02lu", | |||
| 447 | d, d > 1 ? "s" : "", h, m, s); | |||
| 448 | else | |||
| 449 | sprintf(buffer, "%02lu:%02lu:%02lu", h, m, s); | |||
| 450 | ||||
| 451 | if (t == -1) | |||
| 452 | sprintf(buffer, "unknown"); | |||
| 453 | ||||
| 454 | return buffer; | |||
| 455 | } | |||
| 456 | ||||
| 457 | const char *apm_time_nosec(time_t t) | |||
| 458 | { | |||
| 459 | static char buffer[128]; | |||
| 460 | unsigned long s, m, h, d; | |||
| 461 | ||||
| 462 | d = t / SEC_PER_DAY(60*60*24); | |||
| 463 | t -= d * SEC_PER_DAY(60*60*24); | |||
| 464 | h = t / SEC_PER_HOUR(60*60); | |||
| 465 | t -= h * SEC_PER_HOUR(60*60); | |||
| 466 | m = t / SEC_PER_MIN(60); | |||
| 467 | t -= m * SEC_PER_MIN(60); | |||
| 468 | s = t; | |||
| 469 | ||||
| 470 | if (s > 30) | |||
| 471 | ++m; | |||
| 472 | ||||
| 473 | if (d) | |||
| 474 | sprintf(buffer, "%lu day%s, %lu:%02lu", | |||
| 475 | d, d > 1 ? "s" : "", h, m); | |||
| 476 | else | |||
| 477 | sprintf(buffer, "%lu:%02lu", h, m); | |||
| 478 | ||||
| 479 | if (t == -1) | |||
| 480 | sprintf(buffer, "unknown"); | |||
| 481 | ||||
| 482 | return buffer; | |||
| 483 | } |