home *** CD-ROM | disk | FTP | other *** search
- /*
- ** utilities.C - utility functions
- **
- ** utilities.C utilities.C 1.51 Delta\'d: 15:10:14 10/9/92 Mike Lijewski, CNSF
- **
- ** Copyright \(c\) 1991, 1992 Cornell University
- ** All rights reserved.
- **
- ** Redistribution and use in source and binary forms are permitted
- ** provided that: \(1\) source distributions retain this entire copyright
- ** notice and comment, and \(2\) distributions including binaries display
- ** the following acknowledgement: ``This product includes software
- ** developed by Cornell University\'\' in the documentation or other
- ** materials provided with the distribution and in all advertising
- ** materials mentioning features or use of this software. Neither the
- ** name of the University nor the names of its contributors may be used
- ** to endorse or promote products derived from this software without
- ** specific prior written permission.
- **
- ** THIS SOFTWARE IS PROVIDED ``AS IS\'\' AND WITHOUT ANY EXPRESS OR
- ** IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
- ** WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
- */
-
- #include <ctype.h>
-
- #ifndef _IBMR2
- #include <libc.h>
- #endif
-
- #include <fcntl.h>
- #include <osfcn.h>
- #include <pwd.h>
- #include <signal.h>
- #include <stdarg.h>
- #include <stdlib.h>
- #include <stdio.h>
-
- #ifndef _IBMR2
- #include <unistd.h>
- #endif
-
- // access\(2\) is prototyped in <sys/access.h> on RS/6000s
- #ifdef _IBMR2
- #include <sys/access.h>
- #include <sys/lockf.h>
- #endif
-
- #include <sys/stat.h>
- #include <sys/types.h>
- #ifdef ESIX
- typedef int pid_t;
- #endif /*ESIX*/
- #include <sys/wait.h>
- #include <string.h>
- #include <sys/errno.h>
-
- // this header file is badly busted on RS/6000s
- #ifndef _IBMR2
- #include <unistd.h>
- #endif
-
- #include "classes.h"
- #include "display.h"
- #include "lister.h"
- #include "problem.h"
- #include "utilities.h"
-
- /* remove this once GNU gets it fixed -- this stuff should be in <unistd.h> */
- #ifdef __GNUG__
- extern "C" int lockf(int, int, long);
- extern "C" int flock(int, int);
- #ifndef LOCK_UN
- #define LOCK_UN 8
- #endif
- #ifndef LOCK_EX
- #define LOCK_EX 2
- #endif
- #ifndef F_LOCK
- #define F_LOCK 1
- #endif
- #ifndef F_ULOCK
- #define F_ULOCK 0
- #endif
- #endif /*__GNUG__*/
-
- const int KEY_CR = '\r'; // carriage return
- const int KEY_BKSP = '\b'; // backslash
- const int KEY_CTL_L = '\f'; // repaint screen -- CTR-L
-
- /*
- ** fgetline - returns a pointer to the start of a line read from fp,
- ** or the null pointer if we hit eof or get an error from
- ** fgets\(\). Exits if new\(\) fails. Strips the newline from
- ** the line. Caller should free memory if desired. `size\'
- ** is the expected length of the line to be read.
- */
-
- char *fgetline(FILE *fp, int size)
- {
- char *buffer = new char[size];
-
- char *result= fgets(buffer, size, fp);
- if (result == 0)
- {
- //
- // Either error or at eof.
- //
- DELETE buffer;
- return 0;
- }
-
- if (buffer[strlen(buffer) - 1] != '\n' && !feof(fp))
- {
- //
- // Longer line than buffer can hold.
- //
- char *restofline = fgetline(fp, size);
-
- if (restofline == 0) return 0; // eof or error
-
- char *longline = new char[strlen(buffer) + strlen(restofline) + 1];
- (void)strcat(strcpy(longline, buffer), restofline);
-
- DELETE restofline;
- DELETE buffer;
-
- if (longline[strlen(longline) - 1] == '\n')
- longline[strlen(longline) - 1] = '\0';
-
- return longline;
- }
- else
- {
- if (buffer[strlen(buffer) - 1] == '\n')
- buffer[strlen(buffer) - 1] = '\0';
- return buffer;
- }
- }
-
- /*
- ** display_string - prints a string to the given the display, guaranteeing not
- ** to print more than columns\(\) characters. If the string
- ** exceeds the width of the window, a `!\' is placed in
- ** the final column. `len\' is the length of the string,
- ** if known, which defaults to zero. `offset\', which
- ** defaults to zero, is non-zero in those cases where we\'ve
- ** already printed `offset\' characters to the screen line.
- ** We never call this when trying to write to the last
- ** row on the screen. That is the dominion of message\(\).
- */
-
- void display_string(const char *str, int length, int offset)
- {
- int len = (length == 0 ? (int) strlen(str) : length) + offset;
-
- if (len < columns())
- {
- (void)fputs(str, stdout);
- cursor_wrap();
- }
- else if (len > columns())
- {
- (void)printf("%*.*s%c", columns() - offset - 1, columns() - offset - 1,
- str, '!');
- if (!AM || XN) cursor_wrap();
- }
- else
- {
- (void)fputs(str, stdout);
- if (!AM || XN) cursor_wrap();
- }
- }
-
- /*
- ** error - Prints error message so it can be read. This is the error
- ** function we call once we\'ve initialized the display.
- */
-
- void error(const char *format, ...)
- {
- va_list ap;
- va_start(ap, format);
- clear_message_line();
- (void) vfprintf(stdout, format, ap);
- cursor_wrap();
- va_end(ap);
- synch_display();
- term_display();
- exit(1);
- }
-
- /*
- ** eat_a_character - prints a message and then reads and
- ** discards a character from the keyboard.
- ** signals interrupt read\(2\); in fact we rely on this to
- ** correctly deal with SIGTSTP and SIGWINCH. If we get
- ** an EINTR, we reprint the message and keep trying to
- ** read a character. if `standout\' is true, the message
- ** is printed in standout mode; it defaults to false.
- */
-
- void eat_a_character(const char *msg, void (*redisplay)(), int standout)
- {
- char c;
- while (1)
- {
- // read a character dealing with interruption
- if (standout) enter_standout_mode();
- message(msg);
- if (standout) end_standout_mode();
- if (resumingAfterSuspension ||
- #ifdef SIGWINCH
- windowSizeChanged ||
- #endif
- read(0, &c, 1) < 0 || // assume only fails when errno==EINTR
- c == KEY_CTL_L)
- {
- #ifdef SIGWINCH
- if (windowSizeChanged) { windowSizeChanged = 0; adjust_window(); }
- #endif
- resumingAfterSuspension = 0;
- redisplay();
- if (standout) enter_standout_mode();
- message(msg);
- if (standout) end_standout_mode();
- }
- else
- return;
- }
- }
-
- /*
- ** execute - executes command using exec\(2\). Returns 1 if the exec
- ** went OK, otherwise it returns 0. Assumes that the exec\'d
- ** program wants our open file descriptors.
- ** Forces the effective uid to the real uid in the child.
- */
-
- int execute(const char *file, const char *argv[])
- {
- unsetraw();
- unset_signals();
- int status;
- pid_t pid = fork();
- switch(pid)
- {
- case -1: // error
- return 0;
- case 0: // in the child
- if (setuid(getuid()) < 0)
- error("file %s, line %d, setuid() failed" __FILE__, __LINE__);
- execvp(file, (char *const *)argv);
- // exec failed
- exit(1);
- default: // in the parent
- waitpid(pid, &status, 0);
- set_signals();
- setraw();
- return status == 0 ? 1 : 0;
- }
- }
-
- #ifdef SIGWINCH
-
- /*
- ** winch - set flag indicating window size changed.
- */
-
- int windowSizeChanged; // should be a sig_atomic_t
-
- void winch(int)
- {
- (void)signal(SIGWINCH, SIG_IGN);
- windowSizeChanged = 1;
- (void)signal(SIGWINCH, winch);
- }
-
- #include <sys/ioctl.h>
-
- /*
- ** adjust_window - called to adjust our window after getting a SIGWINCH
- */
-
- void adjust_window()
- {
- #ifdef TIOCGWINSZ
- struct winsize w;
- if (ioctl(2, TIOCGWINSZ, (char *)&w) == 0 && w.ws_row > 0) LI = w.ws_row;
- if (ioctl(2, TIOCGWINSZ, (char *)&w) == 0 && w.ws_col > 0) CO = w.ws_col;
- #endif
- if (LI < 5 || CO < 20)
- error("screen too small to be useful");
- }
-
- #endif
-
- /*
- ** prompt - displays `msg\' prompt and then collects the response.
- ** The keys of the response are echoed as they\'re collected.
- ** The response should be delete\'d when no longer needed.
- ** A response can contain any graphical character. Backspace
- ** works as expected. Carriage return indicates the end of
- ** response. Non-graphical characters are ignored. If we
- ** get suspended and resumed in prompt\(\), `redisplay\' is
- ** the function to call to fixed up all but the message lines
- ** of the display. We rely on signals interrupting read\(2\).
- */
-
- char *prompt(const char *msg, void (*redisplay)())
- {
- size_t written = 0; // number of characters written to message line
- size_t len = strlen(msg);
- String nmsg(msg);
-
- clear_message_line();
-
- if (len < columns())
- {
- (void)fputs(nmsg, stdout);
- written = len;
- }
- else
- {
- // Leave space for columns\(\)/2 + 1 characters.
- (void)fputs((const char *)nmsg + (len-columns()/2+1), stdout);
- written = columns()/2 - 1;
- }
- synch_display();
-
- //
- // We never echo into the last position in the message window.
- //
- size_t space_available = columns() - written; // available spaces in line
- char *response = new char[space_available + 1];
- size_t pos = 0; // index of next character in `response\'
-
- char key;
- for (;;)
- {
- if (resumingAfterSuspension ||
- #ifdef SIGWINCH
- windowSizeChanged ||
- #endif
- read(0, &key, 1) < 0 || // assume only fails when errno == EINTR
- key == KEY_CTL_L)
- {
- #ifdef SIGWINCH
- if (windowSizeChanged) { windowSizeChanged = 0; adjust_window(); }
- #endif
- resumingAfterSuspension = 0;
- redisplay();
- clear_message_line(); // make sure we\'re on the message line
- response[pos] = 0;
- if (pos + len < columns())
- {
- //
- // Output message and response-to-date.
- //
- (void)fputs(nmsg, stdout);
- (void)fputs(response, stdout);
- space_available = columns() - pos - len;
- }
- else if (pos < columns())
- {
- //
- // Display the response.
- //
- (void)fputs(response, stdout);
- space_available = columns() - strlen(response);
- }
- else
- {
- //
- // Display the backend of the response.
- //
- (void)fputs(&response[pos - columns()/2 + 1], stdout);
- space_available = columns()/2 + 1;
- }
- synch_display();
- }
- else if (isprint(key))
- {
- //
- // echo character to message window and wait for another
- //
- response[pos++] = key;
- space_available--;
- if (!space_available)
- {
- //
- // Need to allocate more room for the response.
- // Note that strlen\(response\) == pos
- //
- space_available = columns()/2 + 1;
- char *nresponse = new char[pos + space_available + 1];
- response[pos] = 0; // stringify response
- (void)strcpy(nresponse, response);
-
- DELETE response;
- response = nresponse;
-
- //
- // Shift prompt in message window so we
- // always have the end in view to which we\'re
- // adding characters as they\'re typed.
- //
- clear_message_line();
- (void)fputs(&response[pos - columns()/2 + 1], stdout);
- key = 0; // nullify key
- }
- else
- {
- putchar(key);
- key = 0; // nullify key
- }
- synch_display();
- }
- else
- switch (key)
- {
- case KEY_CR: // we have the complete response
- response[pos] = 0;
- clear_message_line();
- synch_display();
- return response;
- case KEY_BKSP: // back up one character
- if (pos == 0) { ding(); break; }
- backspace();
- DC ? delete_char_at_cursor() : clear_to_end_of_line();
- --pos;
- ++space_available;
- if (space_available == columns())
- {
- //
- // The only way this can happen is if we
- // had previously shifted the response to the left.
- // Now we must shift the response to the right.
- //
- clear_message_line();
- response[pos] = 0;
- if (pos + len < columns())
- {
- //
- // Output message and response-to-date.
- //
- (void)fputs(nmsg, stdout);
- (void)fputs(response, stdout);
- space_available = columns() - pos - len;
- }
- else if (pos < columns())
- {
- //
- // Display the response.
- //
- (void)fputs(response, stdout);
- space_available = columns() - strlen(response);
- }
- else
- {
- //
- // Display the backend of the response
- //
- (void)fputs(&response[pos - columns()/2 + 1], stdout);
- space_available = columns()/2 + 1;
- }
- }
- synch_display();
- break;
- default: ding(); break; // ignore other characters
- }
- }
- }
-
- /*
- ** message - prints a message on the last line of the screen.
- ** It is up to the calling process to put the cursor
- ** back where it belongs. Synchs the display. It can
- ** be called as either:
- **
- ** message\(msg\);
- ** or
- ** message\(fmt, str\);
- **
- ** In the later case it must be the case that the format `fmt\'
- ** has exactly one `%\' into which the `str\' will be substituted
- ** as in the ?printf\(\) functions. Prints in standout mode.
- */
-
- // the definition -- declared in utilities.h
- int message_window_dirty = 0;
-
- void message(const char *fmt, const char *str)
- {
- char *msg; // the complete message to be output
- int allocated = 0; // was `msg\' allocated in new\(\) space?
-
- clear_message_line();
-
- if (str)
- {
- msg = new char[strlen(fmt) + strlen(str) + 1];
- const char *token = strchr(fmt, '%');
- if (token == 0)
- //
- // This shouldn\'t happen. But if it does, let\'s
- // just print the format `fmt\'.
- //
- msg = (char *)fmt;
- else
- {
- (void)strncpy(msg, fmt, token - fmt);
- msg[token - fmt] = 0; // strncpy doesn\'t nullify the string
- (void)strcat(msg, str);
- (void)strcat(msg, token + 1);
- allocated = 1;
- }
- }
- else
- msg = (char *)fmt;
-
- if (strlen(msg) < columns())
- (void)fputs(msg, stdout);
- else
- (void)printf("%*.*s", columns() - 1, columns() - 1, msg);
-
- synch_display();
- message_window_dirty = 1;
- if (allocated) DELETE msg;
- }
-
- /*
- ** yes_or_no - returns true if a \'y\' or \'Y\' is typed in response to
- ** the msg. We deal with being suspended and resumed.
- **
- ** defResponse is the assumed default response.
- **
- ** defResponse == Yes ==> that return value is true unless
- ** \'n\' or \'N\' is typed; else it
- ** returns false.
- **
- ** defResponse == No ==> that return value is true only if
- ** \'y\' or \'Y\' is typed; else it
- ** return false.
- **
- ** If standout is true the message is displayed in standout mode.
- **
- ** It is assumed that the message that is printed somehow indicates
- ** whether the default response is true or false.
- */
-
- int yes_or_no(const char *msg,
- void (*redisplay)(),
- Response defResponse,
- int standout)
- {
- if (standout) enter_standout_mode();
- message(msg);
- if (standout) end_standout_mode();
-
- char key;
- while (1)
- //
- // read a character dealing with interruption
- //
- if (resumingAfterSuspension ||
- #ifdef SIGWINCH
- windowSizeChanged ||
- #endif
- read(0, &key, 1) < 0 || // assume only fails when errno==EINTR
- key == KEY_CTL_L)
- {
- #ifdef SIGWINCH
- if (windowSizeChanged)
- {
- windowSizeChanged = 0;
- adjust_window();
- }
- #endif
- resumingAfterSuspension = 0;
- redisplay();
- if (standout) enter_standout_mode();
- message(msg);
- if (standout) end_standout_mode();
- }
- else
- break;
- clear_message_line();
- synch_display();
-
- switch (defResponse)
- {
- case Yes:
- return !(key == 'n' || key == 'N');
- case No:
- return key == 'y' || key == 'Y';
- }
- }
-
- /*
- ** lock_file - lock the file opened with file descriptor `fd\'.
- ** Exits on error.
- */
-
- void lock_file(int fd)
- {
- if (lseek(fd, 0, 0) < 0) error("lseek() failed");
- #ifdef FLOCK
- if (flock(fd, LOCK_EX) < 0) error("flock - LOCK_EX");
- #else
- if (lockf(fd, F_LOCK, 0) < 0) error("lockf - F_LOCK");
- #endif
- }
-
- /*
- ** unlock_file - unlock file with file descriptor `fd\'.
- ** Exits on error.
- */
-
- void unlock_file(int fd)
- {
- if (lseek(fd, 0, 0) < 0) error("lseek() failed");
- #ifdef FLOCK
- if (flock(fd, LOCK_UN) < 0) error("flock - LOCK_UN");
- #else
- if (lockf(fd, F_ULOCK, 0) < 0) error("lockf - F_ULOCK");
- #endif
- }
-
- /*
- ** cleanup - cleanup and exit after a SIGHUP, SIGTERM, SIGQUIT or SIGINT
- */
-
- void cleanup(int) { term_display(); exit(0); }
-
- /*
- ** username - returns the username pertaining to the real uid.
- ** Exits on error;
- */
-
- const char *username()
- {
- static String user;
- if (user == "")
- {
- struct passwd *entry = getpwuid(getuid());
- if (!entry)
- error("file %s, line %d, getpwuid() failed", __FILE__, __LINE__);
- user = entry->pw_name;
- }
- return user;
- }
-
- /*
- ** write_to_pipe - writes the data to the pipe on the file descriptor.
- ** Exits on error.
- */
-
- void write_to_pipe(int fd, const char *data, int size)
- {
- int nwritten = 0;
- while (size > 0)
- {
- nwritten = write(fd, data, size);
- if (nwritten <= 0)
- error("file %s, line %d, write() failed", __FILE__, __LINE__);
- size -= nwritten;
- data += nwritten;
- }
-
- }
-
- /*
- ** read_file - reads the file pointed to by `fp\' into the array `lines\'.
- ** `lines\' must have been previously allocated in the caller,
- ** to size `size\'. `len\' is the expected length of the lines
- ** in the file. `pos\' is the position in `lines\'
- ** to which we start adding the lines; it defaults to zero.
- ** In general, this will be zero, but in one case when I\'m
- ** building an argv for an execvp\(\), I want a non-zero offset.
- ** Returns the number of lines in `lines\' or -1 on error.
- ** `lines\' is null-terminated.
- **
- ** This is used only for reading files containing
- ** relatively few lines.
- */
-
- int read_file(FILE *fp, char** &lines, int size, int linelen, int pos)
- {
- const int chunksize = 20; // chunksize to `grow\' by
- int nlines = 0; // number of lines added to `lines\'
-
- char *line = fgetline(fp, linelen);
- for (; line; line = fgetline(fp, linelen))
- {
- // skip comment lines
- if (*line == '#') { DELETE line; continue; }
- // strip off newline
- if (line[strlen(line) - 1] == '\n') line[strlen(line) - 1] = 0;
- lines[pos++] = line;
- nlines++;
- if (pos == size)
- {
- // grow `Areas\'
- char **newspace = new char*[size += chunksize];
- for (int i = 0; i < pos; i++) newspace[i] = lines[i];
- DELETE lines;
- lines = newspace;
- }
- }
- if (feof(fp) && !ferror(fp))
- {
- // null terminate `lines\'
- if (pos == size)
- {
- // got to grow one more
- char **newspace = new char*[size + 1];
- for (int i = 0; i < pos; i++) newspace[i] = lines[i];
- DELETE lines;
- lines = newspace;
- }
- lines[pos] = 0;
- return nlines;
- }
- else
- return -1;
- }
-
- /*
- ** tokenize - returns a null-terminated vector of the words in `line\'
- ** The vector and its elements are in volatile storage
- ** which we manage here.
- */
-
- const char **tokenize(const char *line, const char *separators)
- {
- //
- // Since strtok modifies it\'s argument, we use a copy of `line\'.
- //
- static char *newline; // volatile storage of vector elements
- DELETE newline;
- newline = new char[strlen(line) + 1];
- (void)strcpy(newline, line);
-
- const int chunksize = 5; // chunksize to `grow\' by
- int size = chunksize; // total size of vector
- int nwords = 0; // number of words in vector
- static char **words; // volatile storage for the word pointers
- DELETE words;
- words = new char*[chunksize];
-
- if ((words[nwords++] = strtok(newline, separators)) == 0)
- return (const char **)words;
-
- while (words[nwords++] = strtok(0, separators))
- if (nwords == size)
- {
- // Grow "words".
- char **newspace = new char*[size += chunksize];
- for (int i = 0; i < nwords; i++) newspace[i] = words[i];
- DELETE words;
- words = newspace;
- }
- return (const char **)words;
- }
-
- /*
- ** read_and_exec_perm - returns non-zero if we have read and execute
- ** permission on the file, otherwise 0.
- ** Returns 0 on error.
- */
-
- int read_and_exec_perm(const char *file)
- {
- return access(file, R_OK | X_OK) == -1 ? 0 : 1;
- }
-
- /*
- ** expand_tilde - expects a string of the form "~ ...".
- ** Returns a new string in volatile storage
- ** with the user\'s home directory in place of the `~\'.
- ** The user\'s home directory is always appended
- ** in the form: "/usr/staff/mjlx"; a slash is not added to
- ** the end of the home directory string. Returns the original
- ** string if we cannot get the user\'s home directory.
- */
-
- const char *expand_tilde(char *str)
- {
- static char *home = getenv("HOME");
- if (home == NULL)
- {
- struct passwd *user = getpwuid(getuid());
- if (user == NULL) return str;
- home = user->pw_dir;
- }
- if (*str != '~') return str;
- static String expansion;
- expansion = String(home) + (str + 1);
- return expansion;
- }
-
- /*
- ** update_screen_line
- **
- ** `oldline\' is what is currently on the screen in row `y\'
- ** `newline\' is what we want on the screen in row `y\'
- **
- ** We make a good attempt to optimize the output of characters to
- ** the screen. We want to display `newline\' on the screen,
- ** assuming `oldline\' is what is currently displayed. This
- ** will be "good" if `oldline\' and `newline\' are quite similar.
- ** That is to say, this should only be called when there is an
- ** expectation that `oldline\' and `newline\' are "almost" the same.
- */
-
- void update_screen_line(const char *oldline, const char *newline, int y)
- {
- if (strcmp(oldline, newline) == 0) return;
-
- size_t olen = strlen(oldline);
- size_t nlen = strlen(newline);
- size_t len = olen < nlen ? olen : nlen;
-
- //
- // Never display more than columns\(\) characters.
- //
- int chop = 0; // do we need to chop off the tail?
- if (len > columns()) { chop = 1; len = columns(); }
-
- char *equal = new char[len];
-
- //
- // How similar are the two strings?
- //
- int differences = 0;
- for (int i = 0; i < len; i++) equal[i] = 1;
- for (i = 0; i < len; i++)
- if (oldline[i] != newline[i]) { differences++; equal[i] = 0; }
-
- if (differences > columns()/2)
- {
- //
- // We just display the new line.
- //
- clear_to_end_of_line();
- (void)fputs(newline, stdout);
- DELETE equal;
- return;
- }
-
- if (!OS)
- {
- //
- // We can just overwrite the old with the new.
- //
- int last = -2; // position of last character written
- for (i = 0; i < len; i++)
- {
- if (equal[i]) continue;
- if (i - 1 != last) move_cursor(y, i);
- (i == len - 1 && chop) ? putchar('!') : putchar(newline[i]);
- last = i;
- }
- if (nlen > olen)
- {
- //
- // Have more characters to output.
- //
- chop = len > columns();
- move_cursor(y, i);
- for (i = (int)len; i < nlen && i < columns(); i++)
- (i == columns()-1 && chop) ? putchar('!') : putchar(newline[i]);
- }
- else if (nlen < olen)
- {
- move_cursor(y, i);
- clear_to_end_of_line();
- }
- }
- else
- {
- //
- // We can\'t overwrite. Truncate at first difference.
- //
- int first = 0;
- for (i = 0; i < len; i++)
- if (!equal[i]) { first = i; break; }
- move_cursor(y, i);
- clear_to_end_of_line();
- for (; i < nlen && i < columns(); i++)
- (i == columns() - 1) ? putchar('!') : putchar(newline[i]);
- }
- DELETE equal;
- }
-
- /*
- ** update_modeline - this routine concatenates the two strings
- ** into the modeline. The modeline
- ** is displayed in standout mode if possible.
- ** We never put more than columns\(\) characters into
- ** the modeline. The modeline is the penultimate
- ** line on the terminal screen. It does not
- ** synch the display. If head == tail == 0, we
- ** just display the old modeline. This happens
- ** if for some reason we had to clear the screen.
- */
-
- // the current modeline
- char *current_modeline;
-
- void update_modeline(const char *head, const char *tail)
- {
- move_to_modeline();
- enter_standout_mode();
-
- if (head == 0) // actually, head == tail == 0
- {
- // Redisplay old modeline.
- (void)fputs(current_modeline, stdout);
- end_standout_mode();
- return;
- }
-
- int len = (int) strlen(head);
- char *new_modeline = new char[columns() + 1];
- (void)strncpy(new_modeline, head, columns());
- new_modeline[columns()] = 0; // ensure it\'s null-terminated
-
- if (len < columns())
- {
- //
- // Write exactly columns\(\) characters to modeline.
- //
- for (int i = len; i < columns() - 1 && tail && *tail; i++, tail++)
- new_modeline[i] = *tail;
- if (i < columns() - 1)
- {
- new_modeline[i++] = ' ';
- for (; i < columns(); i++) new_modeline[i] = '-';
- }
- else if (tail && *tail)
- //
- // The tail was overly long. Put a \'!\' in the last space
- // on the modeline to signify truncation.
- //
- new_modeline[columns() - 1] = '!';
- else
- //
- // Here len == columns\(\)-1 && there is nothing else in tail.
- //
- new_modeline[columns() - 1] = ' ';
- }
- else if (len > columns())
- new_modeline[columns() - 1] = '!';
-
- if (current_modeline)
- {
- update_screen_line(current_modeline, new_modeline, rows() - 2);
- DELETE current_modeline;
- }
- else
- (void)fputs(new_modeline, stdout);
-
- current_modeline = new_modeline;
- end_standout_mode();
- }
-
- /*
- ** lines_displayed - returns the number of lines in the DList
- ** currently displayed on the screen.
- */
-
- int lines_displayed(DList *dl)
- {
- DLink *ln = dl->firstLine();
- for (int i = 1; ln != dl->lastLine(); i++, ln = ln->next()) ;
- return i;
- }
-
- /*
- ** get_prob_number - returns the prob# of the current line of the DList
- ** in volatile storage. The line is of the form:
- **
- ** prob# ...
- **
- ** We must know INTIMATELY how summary_lines\(\)
- ** formats the output.
- */
-
- char *get_prob_number(const DList *dl)
- {
- static char* number = 0; // our volatile storage
- DELETE number; // release our volatile storage
- const char* begin = dl->currLine()->line();
-
- // step over any spaces preceding Prob #
- while (*begin == ' ') begin++;
-
- const char* end = strchr(begin, ' ');
- int len = end - begin;
- number = new char[len + 1];
- (void)strncpy(number, begin, len);
- number[len] = 0; // stringify it
-
- return number;
- }
-
- /*
- ** leftshift_current_line - shifts the current line in DList left until
- ** its tail is visible.
- */
-
- void leftshift_current_line(DList *dl)
- {
- int inc = dl->currLine()->length()-columns()+1;
- move_cursor(dl->savedYPos(), 0);
- clear_to_end_of_line();
- display_string(&(dl->currLine()->line())[inc],columns()-1);
- dl->saveYXPos(dl->savedYPos(), max(goal_column(dl)-inc, 0));
- move_cursor(dl->savedYPos(), dl->savedXPos());
- }
-
- /*
- ** rightshift_current_line - rightshifts current line to "natural" position.
- */
-
- void rightshift_current_line(DList *dl)
- {
- move_cursor(dl->savedYPos(), 0);
- clear_to_end_of_line();
- display_string(dl->currLine()->line(), dl->currLine()->length());
- dl->saveYXPos(dl->savedYPos(), goal_column(dl));
- move_cursor(dl->savedYPos(), dl->savedXPos());
- }
-
- /*
- ** initial_listing - prints the initial listing screen.
- ** Adjusts firstLine\(\), lastLine\(\) and currLine\(\).
- */
-
- void initial_listing(DList *dl)
- {
- DLink *ln = dl->head();
- dl->setFirst(ln);
- dl->setCurrLine(ln);
- cursor_home();
- for (int i = 0; i < rows() - 2 && ln; ln = ln->next(), i++)
- {
- clear_to_end_of_line();
- display_string(ln->line(), ln->length());
- }
-
- ln ? dl->setLast(ln->prev()) : dl->setLast(dl->tail());
-
- //
- // Don\'t forget to clear any remaining lines in those
- // cases when the screen hasn\'t already been cleared and
- // there are potentially dirty lines remaining.
- //
- move_cursor(i, 0);
- for (; i < rows() - 2; i++) { clear_to_end_of_line(); cursor_down(); }
- }
-
- /*
- ** seconds_in_date - returns the number of seconds in a date string
- ** of the form:
- **
- ** "mmm dd mm:hh:ss yyyy"
- **
- ** This is the format returned by ctime\(3\), with
- ** the leading "day" name chopped off. According to ANSI C,
- ** a long must be able to hold values up to at least
- ** 2147483647. This will keep this function working for
- ** many years into the future. Eventually, CurrentYear will
- ** have to be incremented.
- */
-
- long seconds_in_date(const char *date)
- {
- //
- // days\[i\] is the number of days preceding month `i\',
- // where 0 <= i <= 11
- //
- static const int days[] = { 0,31,59,90,120,151,181,212,243,273,304,334 };
-
- //
- // mhash\[*date + *\(date+1\) + *\(date+2\) - 268\] hashes to an integer
- // in the range 0-11 signifying the month.
- //
- static char mhash[] = { 11, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 7, 0, 0, 2, 0, 0, 3, 0, 0, 9, 4, 8, 0,
- 0, 6, 0, 5, 0, 0, 0, 0, 0, 0, 10 };
-
- const long SecondsPerDay = 86400L;
- const long SecondsPerYear = 31536000L;
- const int CurrentYear = 1992;
- const char *const fmt = "%d";
- long total = 0;
- int tmp, mon = mhash[*date + *(date + 1) + *(date + 2) - 268];
-
- (void)sscanf(date + 13, fmt, &tmp); // seconds
- total += tmp;
- (void)sscanf(date + 10, fmt, &tmp); // minutes
- total += tmp * 60;
- (void)sscanf(date + 7 , fmt, &tmp); // hours
- total += tmp * 3600;
- (void)sscanf(date + 4 , fmt, &tmp); // day of the month
- total += (tmp - 1) * SecondsPerDay;
- // days in months preceding this one
- total += days[mon] * SecondsPerDay;
- (void)sscanf(date + 16, fmt, &tmp); // the year
- // leap year adjustment
- if (tmp % 4 == 0 && tmp % 100 != 0 && mon >= 2) total += SecondsPerDay;
- total += (tmp - CurrentYear) * SecondsPerYear;
- return total;
- }
-
- /*
- ** temporary_file - returns the name of a temporary file. The temporary
- ** is forced to have permission 666. Note that
- ** we force tmpnam\(3\) to store the name for us in
- ** volatile storage.
- */
-
- const char *temporary_file()
- {
- char *file = tmpnam(0);
- if (file == 0)
- error("file %s, line %d, tmpnam() failed", __FILE__, __LINE__);
- int fd;
- if ((fd = open(file, O_RDWR|O_CREAT, 0666)) < 0)
- error("file %s, line %d, open(%s) failed",
- __FILE__, __LINE__, file);
- (void)close(fd);
- return file;
- }
-
- /*
- ** set_signals - set up our signal handlers
- */
-
- void set_signals()
- {
- (void)signal(SIGHUP, cleanup);
- (void)signal(SIGINT, cleanup);
- (void)signal(SIGQUIT, cleanup);
- (void)signal(SIGTERM, cleanup);
- #ifdef SIGTSTP
- (void)signal(SIGTSTP, termstop);
- #endif
- #ifdef SIGWINCH
- (void)signal(SIGWINCH, winch);
- #endif
- }
-
- /*
- ** unset_signals - set signals back to defaults
- */
-
- void unset_signals()
- {
- (void)signal(SIGHUP, SIG_DFL);
- (void)signal(SIGINT, SIG_DFL);
- (void)signal(SIGQUIT, SIG_DFL);
- (void)signal(SIGTERM, SIG_DFL);
- #ifdef SIGTSTP
- (void)signal(SIGTSTP, SIG_DFL);
- #endif
- #ifdef SIGWINCH
- (void)signal(SIGWINCH, SIG_DFL);
- #endif
- }
-
- /*
- ** block_tstp_and_winch - block SIGTSTP and SIGWINCH
- */
-
- #ifdef BSDSIGNALS
- static int oldmask;
- #elif POSIXSIGNALS
- static sigset_t oldset;
- #endif
-
- void block_tstp_and_winch()
- {
- #ifdef BSDSIGNALS
- int oldmask = sigblock(sigmask(SIGTSTP)
- #ifdef SIGWINCH
- | sigmask(SIGWINCH)
- #endif
- );
- #elif POSIXSIGNALS
- sigset_t newset;
- sigemptyset(&newset);
- #ifdef SIGTSTP
- sigaddset(&newset, SIGTSTP);
- #endif
- #ifdef SIGWINCH
- sigaddset(&newset, SIGWINCH);
- #endif
- if (sigprocmask(SIG_BLOCK, &newset, &oldset) < 0)
- error("file %s, line %d, sigprocmask(SIG_BLOCK) failed\n",
- __FILE__, __LINE__);
- #else
- //
- // We use ANSI C signals. These can be "lost" but it\'s the
- // best we can do.
- //
- #ifdef SIGTSTP
- (void)signal(SIGTSTP, SIG_IGN);
- #endif
- #ifdef SIGWINCH
- (void)signal(SIGWINCH, SIG_IGN);
- #endif
- #endif
- }
-
- /*
- ** unblock_tstp_and_winch - unblock SIGTSTP and SIGWINCH
- */
-
- void unblock_tstp_and_winch()
- {
- #ifdef BSDSIGNALS
- (void)sigsetmask(oldmask);
- #elif POSIXSIGNALS
- if (sigprocmask(SIG_SETMASK, &oldset, 0) < 0)
- error("file %s, line %d, sigprocmask(SIG_SETMASK) failed\n",
- __FILE__, __LINE__);
- #else
- #ifdef SIGTSTP
- (void)signal(SIGTSTP, termstop);
- #endif
- #ifdef SIGWINCH
- (void)signal(SIGWINCH, winch);
- #endif
- #endif
- }
-
-