/* This is free software. See LICENSE for terms. * Copyright 2004 - 2015, Patricia Kirk. */ #include <unistd.h> #include <stdio.h> #include <stdlib.h> #include <string.h> #include <errno.h> #include <sys/stat.h> #if defined (__linux__) #define __USE_XOPEN #include <time.h> #undef __USE_XOPEN #endif #include "proto.h" extern struct global g; extern struct data fresh; extern struct lineopts *linelist; extern Display *maindisplay; extern Atom wmdeletewindow, wmprotocols; char smalbuf[SMALBUF]; struct keyval { int key; int value; }; int keyCheck(int key) { struct keyval keys[] = { /* Esc */ {9, 99}, /* Top row */ {10, 1}, {11, 2}, {12, 3}, {13, 4}, {14, 5}, {15, 6}, {16, 7}, {17, 8}, {18, 9}, {19, 0}, /* Back space, Enter, Space */ {22, 22}, {36, 36}, {65, 65}, /* Key pad */ {79, 7}, {80, 8}, {81, 9}, {83, 4}, {84, 5}, {85, 6}, {87, 1}, {88, 2}, {89, 3}, {90, 0}, /* Up, Left, Right */ {98, 98}, {100, 100}, {102, 102}, /* Return */ {108, 36}, {0, -1} }; int i=0; for (;; i++) { if (!keys[i].key || (keys[i].key == key)) break; } return keys[i].value; } void runLines(int key, int button, int x, int y) { struct lineopts *line; for (line = linelist; line; line = line->next) { if (line->left <= x && line->right >= x && line->upper <= y && line->lower >= y) { #if WITH_DETACH if (disMount(line)) { break; } else #endif /* WITH_DETACH */ if (itsaTalker(line)) { break; } else if (itsCalendar(line)) { break; } else if (itsaTimer(line, key, button, x)) { break; } else if (itsElapsed(line, key, button, x)) { break; } else if (itsVolume(line, key, button, x, y)) { break; } #if WITH_COMMANDS || WITH_STEPEXEC else if (line->command) { /* Any key or button */ execComm(line->command); break; } #endif /* WITH_COMMANDS || WITH_STEPEXEC */ break; /* from linelist */ } } } void reFresh(int function) { int refresh; refresh = g.refresh; g.refresh = 0; if (function) { checkLines(); g.updaterequest = 1; } else { getVolume(); } g.refresh = refresh; } XEvent event; void checkEvents() { int key; #if WITH_SUID static int shutdown=0; #endif /* WITH_SUID */ while (XPending(maindisplay)) { XNextEvent(maindisplay, &event); switch (event.type) { case KeyPress: key = event.xkey.keycode; if ((key == 31 || key == 53 || key == 59) && (event.xkey.state & ControlMask)) { /* ^q(uit) || ^w(indow) */ gdayMate(NULL, NULL); } else if (event.xkey.state & ShiftMask) { smalbuf[0] = '\0'; if (key == 10 || key == 87) { /* 1 */ key = (g.powerlog = !g.powerlog); snprintf(smalbuf, sizeof(smalbuf), "power state"); } else if (key == 11 || key == 88) { /* 2 */ key = (g.timelog = !g.timelog); snprintf(smalbuf, sizeof(smalbuf), "time sync"); } else if (key == 12 || key == 89) { /* 3 */ key = (g.thermlog = !g.thermlog); snprintf(smalbuf, sizeof(smalbuf), "thermal zone"); } else if (key == 13 || key == 83) { /* 4 */ key = (g.execlog = !g.execlog); snprintf(smalbuf, sizeof(smalbuf), "remote execute"); } if (key == 1) stringcat(smalbuf, " logging is on", sizeof(smalbuf)); else if (!key) stringcat(smalbuf, " logging is off", sizeof(smalbuf)); if (strlen(smalbuf)) logMessage(smalbuf); #if WITH_SUID } else if (g.clickoff) { /* Don`t respond to other keys. */ break; #endif /* WITH_SUID */ } else if (key == 65 && g.refresh > 1) { /* Space in top. */ reFresh(1); break; } else if ((key = keyCheck(key)) >= 0) { /* Key entry for line manipulation. */ if (!calcPhase(key, -1, event.xkey.x, event.xkey.y)) runLines(key, -1, event.xkey.x, event.xkey.y); } break; /* from key press */ case ButtonPress: #if WITH_SUID if (g.clickoff) { /* Click anywhere for shutdown. * Click again to kill shutdown. */ if (!shutdown) { shutdown = execShutdown(g.clicktime); makeNoise(g.clicksound); } else { shutdown = execShutdown(-1); } break; } #endif /* WITH_SUID */ if (!calcPhase(-1, event.xbutton.button, event.xbutton.x, event.xbutton.y)) runLines(-1, event.xbutton.button, event.xbutton.x, event.xbutton.y); break; /* from button press */ case Expose: if (event.xexpose.count == 0) g.updaterequest = 1; break; case ClientMessage: /* Respond to click on close window button. */ if ((event.xclient.message_type == wmprotocols) && (event.xclient.data.l[0] == wmdeletewindow)) gdayMate(NULL, NULL); break; } } } #if WITH_DETACH int disMount(struct lineopts *line) { char *buffer, *ptr; FILE *fd; int yes=0; if (line->command && !strcmp(line->command, "detach")) { yes = 1; if (!g.localhost || !(buffer = calloc(BUFSIZE, sizeof(char)))) return yes; buffer[0] = '\0'; if (g.effectiveuid) { snprintf(buffer, BUFSIZE, "echo Salmon must be run SUID to implement the detach function"); } else if (!strcmp(line->type, "mount")) { /* This is an absolute address to an alleged file system. */ if (!strcmp(line->argument, "/")) { snprintf(buffer, BUFSIZE, "echo Salmon will not un/mount root"); } else if (!strcmp(line->status, "0")) { snprintf(buffer, BUFSIZE, "echo File system %s is not in /etc/fstab", line->argument); } else if (!strcmp(line->status, "1")) { /* The file system is found but not mounted. * line->value has been set with fs_mntops etc. * by mntPoint() in refresh.c */ snprintf(buffer, BUFSIZE, "/sbin/mount %s", line->value); } else if (!strcmp(line->status, "2")) { snprintf(buffer, BUFSIZE, "/sbin/umount -f %s", line->argument); } } else if (!strcmp(line->type, "interface")) { /* This is a network interface. */ if (!strncmp(line->argument, "wlan", 4)) { snprintf(buffer, BUFSIZE, "echo Salmon does not support detaching %s", line->argument); } else if (!strcmp(line->status, "0")) { snprintf(buffer, BUFSIZE, "/sbin/ifconfig %s up", line->argument); } else { snprintf(buffer, BUFSIZE, "/sbin/ifconfig %s down", line->argument); } } if (strlen(buffer)) { if (line->value && strstr(line->value, "nfs")) { /* line->next was spliced into the linelist at the * first pass through mntPoint() and set to type * remoteHost. The status of the host is kept * updated by checkLines() and the icmp echo * reply code. Don`t attempt to toggle the * state if the host is not responding. */ if (parseStrtol(line->next->value, TRUE, "disMount") >= 5) snprintf(buffer, BUFSIZE, "echo Host %s for file system %s is not responding", line->next->argument, line->argument); } seteuid(g.effectiveuid); if ((fd = popen(buffer, "r"))) { if (!strncmp(buffer, "echo ", 5)) { buffer[fread(buffer, sizeof(char), BUFSIZE, fd)] = '\0'; if ((ptr = strchr(buffer, '\n'))) *ptr = '\0'; logMessage(buffer); } pclose(fd); } seteuid(g.realuid); } FREE(buffer); } return yes; } #endif /* WITH_DETACH */ #if defined(__NetBSD__) || defined(__OpenBSD__) int pointless[]={0, 0, 7, 7, 7, 15, 15, 15, 23, 23, 23, 31, 31, 31, 39, 39, 39, 47, 47, 47, 55, 55, 55, 63, 63, 63, 71, 71, 71, 79, 79, 79, 87, 87, 87, 95, 95, 95, 103, 103, 103, 111, 111, 111, 119, 119, 119, 127, 127, 127, 135, 135, 135, 143, 143, 143, 151, 151, 151, 159, 159, 159, 167, 167, 167, 175, 175, 175, 183, 183, 183, 191, 191, 191, 199, 199, 199, 207, 207, 207, 215, 215, 215, 223, 223, 223, 231, 231, 231, 239, 239, 239, 247, 247, 247, 247, 255, 255, 255, 255, 255 }; #endif int itsVolume(struct lineopts *line, int key, int button, int x, int y) { static time_t lasttime=0; time_t timenow=time(NULL); static int step=0, sleep=0; int yes=0, channel, volume[2]; FILE *fd; if ((yes = (line->index == getIndex("volume")))) { if (!step) { #if defined(__NetBSD__) || defined(__OpenBSD__) step = 3; #else step = 1; #endif } if (!strcmp(line->status, "connect")) { #if !defined(__linux__) g.volconn = !g.volconn; #endif return yes; } if (timenow >= (lasttime + 1)) sleep = 0; if (sleep) /* Update the current values. * This is necessary for the * left & right press & hold * action to work. */ reFresh(0); volume[0] = fresh.volume[0]; volume[1] = fresh.volume[1]; channel = parseStrtol(line->value, TRUE, "itsVolume"); if (x <= line->left + (6 * g.xmag)) { #if defined(__NetBSD__) || defined(__OpenBSD__) if (volume[channel] > 95) volume[channel] = 95; #endif volume[channel] -= step; goto sleep; } else if (x >= line->left + (58 * g.xmag)) { #if defined(__NetBSD__) || defined(__OpenBSD__) if (volume[channel] < 3) volume[channel] = 3; #endif volume[channel] += step; #if defined(__NetBSD__) || defined(__OpenBSD__) if (volume[channel] > 95) volume[channel] = 100; #endif sleep: if (!sleep) sleep = 100000; else sleep = 20000; } else { volume[channel] = (((x - line->left) / g.xmag) - 6) * 2; sleep = 0; } if (volume[channel] < 0) volume[channel] = 0; else if (volume[channel] > 100) volume[channel] = 100; #if defined(__NetBSD__) || defined(__OpenBSD__) volume[channel] = pointless[volume[channel] + ((sleep != 0) && (volume[channel] != 100))]; #endif if (g.volconn) { if (channel) volume[0] = volume[1]; else volume[1] = volume[0]; } #if defined(__FreeBSD__) snprintf(smalbuf, SMALBUF, "%s vol %d:%d", g.mixer, volume[0], volume[1]); #elif defined(__linux__) snprintf(smalbuf, SMALBUF, "%s -v %d", g.mixer, volume[0]); #else snprintf(smalbuf, SMALBUF, "%s -w outputs.master=%d,%d", g.mixer, volume[0], volume[1]); #endif if (strlen(smalbuf) && (fd = popen(smalbuf, "r"))) /* execComm() prints scruff to stdout. */ pclose(fd); if (XPending(maindisplay)) { sleep = 0; } else if (sleep) { usleep(sleep); if (!XPending(maindisplay)) XPutBackEvent(maindisplay, &event); } } lasttime = timenow; return yes; } /* Toggle talking clock on and off. * Don`t set yes unless line->setting * is defined. */ int itsaTalker(struct lineopts *line) { int yes=0; if (!strcmp(line->type, "format")) { if (line->setting) { STRDUP(line->buffer, line->setting); FREE(line->setting); yes = 1; } else if (line->buffer) { STRDUP(line->setting, line->buffer); FREE(line->buffer); yes = 1; } } return yes; } /* Move current date to line * on key/pointer event. */ int itsCalendar(struct lineopts *line) { int yes=0, color=0, setting=1, dir=-1; struct lineopts *l1, *ln; if ((yes = (line->index == getIndex("calendar")))) { for (l1 = linelist; l1; l1 = l1->next) { if (l1->index == getIndex("calendar")) break; } for (ln = l1; ln; ln = ln->next) { if (ln->index != getIndex("calendar")) break; setting += dir; if (ln->color) color = ln->color; if (ln == line) dir = 0; } while (l1 && l1->index == getIndex("calendar")) { snprintf(smalbuf, SMALBUF, "%d", setting); STRDUP(l1->setting, smalbuf); if (setting && l1->color) l1->color = 0; else if (!setting) l1->color = color; l1 = l1->next; setting++; } reFresh(1); } return yes; } int itsaTimer(struct lineopts *line, int key, int button, int x) { char showbuf[SHOWBUF]; int yes=0; if ((yes = (line->index >= getIndex("timer") && line->index <= getIndex("reset")))) { if (!line->status) { if (key == 102 || button == 3) { /* Right arrow or right click, start count */ if (line->value) { /* argument value */ STRDUP(line->buffer, line->value); } else if (line->setting) { /* setting value */ STRDUP(line->buffer, line->setting); } else { return yes; } if (!strcmp(line->type, "timer")) { snprintf(showbuf, sizeof(showbuf), "%d", (int)(parseStrtol(line->buffer, TRUE, "itsaTimer") + time(NULL))); STRDUP(line->buffer, showbuf); } STRDUP(line->status, "running"); } else if (key == 99 || key == 98 || button == 2) { /* ESC, up arrow, middle click, edit setting */ if (!line->setting) { STRDUP(line->setting, "0"); } STRDUP(line->buffer, line->setting); STRDUP(line->status, "setting"); if (!strcmp(line->type, "timer")) adjTimer(line, -1, 0, x); else adjAlarm(line, -1, 0, x); } else if (key == 22 || key == 100 || button == 1) { /* Backspace, left arrow, left click, edit value */ if (!line->value) { STRDUP(line->value, "0"); } STRDUP(line->buffer, line->value); STRDUP(line->status, "value"); if (!strcmp(line->type, "timer")) adjTimer(line, -1, 0, x); else adjAlarm(line, -1, 0, x); } } else if (!strcmp(line->status, "value") || !strcmp(line->status, "setting")) { /* In edit mode. */ if (key == 98 || button == 2) { /* Up arrow, start count. */ if (!strcmp(line->type, "timer")) { snprintf(showbuf, sizeof(showbuf), "%d", (int)(parseStrtol(line->buffer, TRUE, "itsaTimer") + time(NULL))); STRDUP(line->buffer, showbuf); } if (!strcmp(line->type, "reset") && !strcmp(line->status, "setting")) { FREE(line->status); } else { STRDUP(line->status, "running"); } } else { if (!strcmp(line->type, "timer")) adjTimer(line, key, button, x); else adjAlarm(line, key, button, x); } } else if (!strcmp(line->status, "running") && (key == 100 || button == 1)) { /* Left arrow or left click, stop count. */ FREE(line->status); } } return yes; } int itsElapsed(struct lineopts *line, int key, int button, int x) { time_t timeval=0, timenow=time(NULL); char showbuf[SHOWBUF]; int yes=0; if ((yes = (!strcmp(line->type, "elapsed")))) { if (!line->status) { /* Any key or button starts. */ snprintf(showbuf, sizeof(showbuf), "%d", (int)timenow); STRDUP(line->value, showbuf); STRDUP(line->status, "running"); } else if (!strcmp(line->status, "running")) { if (key == 98 || button == 2) { /* Up */ STRDUP(line->status, "stopped"); snprintf(showbuf, sizeof(showbuf), "%d", (int)timenow); STRDUP(line->buffer, showbuf); } } else if (!strcmp(line->status, "stopped") || !strcmp(line->status, "clear")) { if (key == 100 || button == 1) { /* Left */ /* Run time. */ timeval = timenow - parseStrtol(line->buffer, TRUE, "itsElapsed"); snprintf(showbuf, sizeof(showbuf), "%d", parseStrtol(line->value, TRUE, "itsElapsed") + (int)timeval); STRDUP(line->value, showbuf); STRDUP(line->status, "running"); } else if (key == 102 || button == 3) { /* Right */ /* Lap time. */ STRDUP(line->status, "running"); FREE(line->buffer); } else if (key == 98 || button == 2) { /* Up */ if (!strcmp(line->status, "stopped")) { STRDUP(line->status, "clear"); } else if (!strcmp(line->status, "clear")) { FREE(line->status); } } } } return yes; } /* This only responds to up, left or right arrow * keys or button clicks. Click on any part of * the window or panel for function. */ int calcPhase(int key, int button, int x, int y) { time_t timenow=time(NULL); struct lineopts *line; int yes=0; if (g.phasecalc) { for (line = linelist; line; line = line->next) { if (line->status && !strcmp(line->type, "display") && x >= line->left && x <= line->right && y >= ((line->panel > 2) * g.psize) && y <= ((line->panel > 2) * g.psize) + g.psize && (key >= 98 || button > 0)) { yes = 1; if (!g.phasetime) g.phasetime = timenow; if (key == 100 || button == 1) /* Previous quarter, static display. */ getNext(g.phasetime, -1); else if (key == 102 || button == 3) /* Next quarter, static display. */ getNext(g.phasetime, 1); else if (key == 98 || button == 2) /* Current status, running display. */ g.phasetime = 0; checkLines(); /* Update the data. */ g.updaterequest = 1; /* Update the display. */ } } } return yes; } /* Convert user date and time into seconds for alarms. * Enter with line->buffer as HH:MM [yy/mm/dd]. * Returns expiration time in seconds. */ time_t getExpiration(struct lineopts *line) { struct tm *timeptr; time_t timenow=time(NULL); int hour=0, minute=0, year=0, month=0, day=0, count; timeptr = localtime(&timenow); timeptr->tm_sec = 0; count = sscanf(line->buffer, "%d:%d %d/%d/%d", &hour, &minute, &year, &month, &day); if (count == 5) { timeptr->tm_hour = hour; timeptr->tm_min = minute; timeptr->tm_year = year + 100; timeptr->tm_mon = month - 1; timeptr->tm_mday = day; } else if (count == 2) { timeptr->tm_hour = hour; timeptr->tm_min = minute; } else { gdayMate("getExpiration() alarm/reset setting", strerror(EINVAL)); } return mktime(timeptr); } /* Convert user time into seconds for timers. * Enter with line->buffer as ddd HH:MM:SS. * Returns duration in seconds. */ time_t getDuration(struct lineopts *line) { int days=0, hours, minutes, seconds; if ((sscanf(line->buffer, "%d:%d:%d", &hours, &minutes, &seconds) == 3) || (sscanf(line->buffer, "%d %d:%d:%d", &days, &hours, &minutes, &seconds) == 4) || (sscanf(line->buffer, "T||%d||%d:%d:%d", &days, &hours, &minutes, &seconds) == 4)) { if (days > 999) days = 999; if (hours > 23) hours = 23; if (minutes > 59) minutes = 59; if (seconds > 59) seconds = 59; } else { gdayMate("getDuration() timer preset", strerror(EINVAL)); } return ((days * 86400) + (hours * 3600) + (minutes * 60) + seconds); } /* Translate key and button events * for adjTimer() and adjAlarm(). */ int transKey(int *key, int *button, int *iskey) { int yes=0; /* Use left and right arrow keys as button events. */ if (*key == 100) { *key = -1; *button = 1; } else if (*key == 102) { *key = -1; *button = 3; } if (*key >= 0 && *key <= 9) { *key += AZERO; *iskey = 1; yes = 1; } else if (*button > 0) { if (*button == 1) *button = -1; else if (*button == 3) *button = 1; yes = 1; } else if (*key == 99) { yes = 1; } return yes; } /* Used for key or button setting timers. */ void adjTimer(struct lineopts *line, int key, int button, int x) { int days, hours, minutes, seconds, mx, space=0, iskey=0; char showbuf[SHOWBUF]; mx = (x - line->left) / g.xmag; splitSeconds(&days, &hours, &minutes, &seconds, parseStrtol(line->buffer, TRUE, "edjTimer")); snprintf(showbuf, sizeof(showbuf), "T||%03d||%02d:%02d:%02d", days, hours, minutes, seconds); if (transKey(&key, &button, &iskey)) { if (mx >= 55) { /* Seconds. */ space = 15; } else if (mx >= 50 && mx < 55) { /* Ten seconds. */ space = 14; } else if (mx >= 43 && mx < 48) { /* Minutes. */ space = 12; } else if (mx >= 38 && mx < 43) { /* Ten minutes. */ space = 11; } else if (mx >= 32 && mx < 37) { /* Hours. */ space = 9; } else if (mx >= 26 && mx < 31) { /* Ten hours. */ space = 8; } else if (mx >= 19 && mx < 24) { /* Days. */ space = 5; } else if (mx >= 14 && mx < 19) { /* Ten days. */ space = 4; } else if (mx >= 9 && mx < 14) { /* One hundred days. */ space = 3; } else if (mx < 8 && button == -1) { FREE(line->status); } if (space) { if (iskey) { showbuf[space] = key; } else { showbuf[space] += button; if (showbuf[space] > ANINE) showbuf[space] = ANINE; else if (showbuf[space] < AZERO) showbuf[space] = AZERO; } } } STRDUP(line->buffer, showbuf); snprintf(showbuf, sizeof(showbuf), "%d", (int)getDuration(line)); STRDUP(line->buffer, showbuf); if (key == 36 && mx < 8) { if (!strcmp(line->status, "setting")) { STRDUP(line->setting, showbuf); } else { STRDUP(line->value, showbuf); } snprintf(showbuf, sizeof(showbuf), "%d", (int)(parseStrtol(line->buffer, TRUE, "adjTimer") + time(NULL))); STRDUP(line->buffer, showbuf); STRDUP(line->status, "running"); } } /* Change the reset interval from the default 24 * hours or user preset on line->type "reset". */ void adjInterval(struct lineopts *line, int key, int button, int x) { char showbuf[SHOWBUF]; time_t timeval=0; int mx, space=0, top=ANINE, iskey=0; int days, hours, minutes; mx = (x - line->left) / g.xmag; timeval = parseStrtol(line->buffer, TRUE, "adjInterval"); splitSeconds(&days, &hours, &minutes, NULL, timeval); snprintf(showbuf, sizeof(showbuf), "Interval|%02d:%02d", hours + (days * 24), minutes); if (transKey(&key, &button, &iskey)) { if (mx >= 58) { /* Minutes */ space = 13; } else if (mx >= 53 && mx < 58) { /* Ten minutes */ space = 12; top = 53; /* 5 */ } else if (mx >= 46 && mx < 51) { /* Hours */ space = 10; } else if (mx >= 41 && mx < 46) { /* Ten hours */ space = 9; } else if (mx < 8) { if (button == -1) { FREE(line->status); } } if (space) { if (iskey) showbuf[space] = key; else showbuf[space] += button; if (showbuf[space] > top) showbuf[space] = top; else if (showbuf[space] < AZERO) showbuf[space] = AZERO; } } sscanf(showbuf, "Interval|%d:%d", &hours, &minutes); timeval = (hours * 3600) + (minutes * 60); snprintf(showbuf, sizeof(showbuf), "%d", (int)timeval); STRDUP(line->buffer, showbuf); if (key == 36 && mx < 8) { /* Enter, set new interval */ if (!timeval) { FREE(line->setting); /* Restore default 24 hours */ } else { STRDUP(line->setting, showbuf); } FREE(line->status); } } void adjAlarm(struct lineopts *line, int key, int button, int x) { struct tm *timeptr=NULL; time_t timeval=0, timenow=time(NULL); char showbuf[SHOWBUF], *ptr; int mx, space=0, top=ANINE, iskey=0; if (!strcmp(line->type, "reset") && !strcmp(line->status, "setting")) { adjInterval(line, key, button, x); return; } mx = (x - line->left) / g.xmag; timeval = parseStrtol(line->buffer, TRUE, "adjAlarm"); if (!timeval) timeval = timenow; timeptr = localtime(&timeval); timeptr->tm_sec = 0; strftime(showbuf, sizeof(showbuf), "A||%m/%d||%H:%M", timeptr); if (!strcmp(line->type, "reset")) showbuf[0] = 'R'; if (key == 99) /* ESC, set to now plus one hour. */ button = 0; /* So it won`t change values. */ if (transKey(&key, &button, &iskey)) { if (mx >= 55) { /* Minutes */ space = 14; } else if (mx >= 50 && mx < 55) { /* Ten minutes */ space = 13; top = 53; /* 5 */ } else if (mx >= 43 && mx < 48) { /* Hours */ space = 11; } else if (mx >= 38 && mx < 43) { /* Ten hours */ space = 10; top = 50; /* 2 */ } else if (mx >= 31 && mx < 36) { /* Days */ space = 7; } else if (mx >= 26 && mx < 31) { /* Ten days */ space = 6; top = 51; /* 3 */ } else if (mx >= 15 && mx < 20) { /* Months */ space = 4; } else if (mx >= 10 && mx < 15) { /* Ten months */ space = 3; top = 49; /* 1 */ } else if (mx < 8) { if (key == 99) { /* ESC */ timeval = timenow + 3600; timeptr = localtime(&timeval); strftime(showbuf, sizeof(showbuf), " ||%m/%d||%H:%M", timeptr); } else if (button == -1) { FREE(line->status); } } if (space) { if (iskey) showbuf[space] = key; else showbuf[space] += button; if (showbuf[space] > top) showbuf[space] = top; else if (showbuf[space] < AZERO) showbuf[space] = AZERO; } } timeptr = localtime(&timenow); timeptr->tm_sec = 0; ptr = showbuf; ptr++; /* Past the A or D */ strptime(ptr, "||%m/%d||%H:%M", timeptr); timeval = mktime(timeptr); snprintf(showbuf, sizeof(showbuf), "%d", (int)timeval); STRDUP(line->buffer, showbuf); if (key == 36 && mx < 8) { if (!strcmp(line->status, "setting")) { STRDUP(line->setting, showbuf); } else { STRDUP(line->value, showbuf); } STRDUP(line->status, "running"); } }