diff options
author | Markus Uhlin <markus@nifty-networks.net> | 2025-09-15 18:50:32 +0200 |
---|---|---|
committer | Markus Uhlin <markus@nifty-networks.net> | 2025-09-15 18:50:32 +0200 |
commit | c3eee8e333866d92e5fd94ae83cef618758c11bb (patch) | |
tree | 234a06fd90bd61a6668490a0cbf8870e6c674b81 /FICS/utils.c |
FICS RPBLC v1.4.61.4.6
Diffstat (limited to 'FICS/utils.c')
-rw-r--r-- | FICS/utils.c | 1214 |
1 files changed, 1214 insertions, 0 deletions
diff --git a/FICS/utils.c b/FICS/utils.c new file mode 100644 index 0000000..4da2411 --- /dev/null +++ b/FICS/utils.c @@ -0,0 +1,1214 @@ +/* utils.c + * + */ + +/* + fics - An internet chess server. + Copyright (C) 1993 Richard V. Nash + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. +*/ + +/* Revision history: + name email yy/mm/dd Change + Richard Nash 93/10/22 Created + Markus Uhlin 23/12/10 Fixed compiler warnings (plus more) + Markus Uhlin 23/12/10 Deleted check_emailaddr() + Markus Uhlin 23/12/17 Reformatted functions + Markus Uhlin 23/12/25 Revised + Markus Uhlin 24/03/16 Replaced unbounded string + handling functions. + Markus Uhlin 24/07/05 Fixed unusual struct allocations + and replaced strcpy() with strlcpy(). + Markus Uhlin 24/07/07 Made certain functions private + Markus Uhlin 24/07/07 Added parameter 'size' to + psprintf_highlight(). + Markus Uhlin 24/08/11 Improved fix_time(). + Markus Uhlin 24/12/01 Usage of sizeof and fixed + ignored fgets() retvals. + Markus Uhlin 24/12/02 Fixed a possible array overrun + in truncate_file(). + Markus Uhlin 25/03/09 truncate_file: + fixed null ptr dereference. + Markus Uhlin 25/04/06 Fixed Clang Tidy warnings. + Markus Uhlin 25/07/21 Replaced non-reentrant functions + with their corresponding thread + safe version. + Markus Uhlin 25/07/28 truncate_file: restricted file + permissions upon creation. +*/ + +#include "stdinclude.h" +#include "common.h" + +#include <ctype.h> +#include <err.h> +#include <errno.h> +#include <fcntl.h> +#include <string.h> + +#include "config.h" +#include "ficsmain.h" +#include "network.h" +#include "playerdb.h" +#include "rmalloc.h" +#include "utils.h" + +#if __linux__ +#include <bsd/string.h> +#endif + +struct t_tree { + struct t_tree *left, *right; + char *name; +}; + +struct t_dirs { + struct t_dirs *left, *right; + time_t mtime; + struct t_tree *files; + char *name; +}; + +PRIVATE char** t_buffer = NULL; +PRIVATE int t_buffersize = 0; + +PUBLIC bool +is_valid_filename(const char *name, const bool allow_hidden) +{ + if (name == NULL || strcmp(name, "") == 0) + return false; + if (!allow_hidden && name[0] == '.') + return false; + if (strstr(name, "..") || strchr(name, '/') || strchr(name, '\\') || + name[0] == '/') + return false; + for (const char *p = name; *p; ++p) { + if (isspace((unsigned char)*p) || iscntrl((unsigned char)*p)) + return false; + } + for (const char *p = name; *p; ++p) { + if (!isalnum((unsigned char)*p) && *p != '-' && *p != '_' && + *p != '.') + return false; + } + return true; +} + +/* + * Function to validate login names for file path usage + */ +PUBLIC bool +is_valid_login_name(const char *login) +{ + if (login == NULL || login[0] == '\0' || + strstr(login, "..") || strchr(login, '/') || strchr(login, '\\')) + return false; + + for (const char *p = login; *p; ++p) { + if (!((*p >= 'a' && *p <= 'z') || + (*p >= 'A' && *p <= 'Z') || + (*p >= '0' && *p <= '9') || + *p == '_')) + return false; + } + + return true; +} + +PUBLIC int +count_lines(FILE *fp) +{ + int c, nl = 0; + + while ((c = fgetc(fp)) != EOF) + if (c == '\n') + ++nl; + return nl; +} + +PUBLIC int +iswhitespace(int c) +{ + if (c < ' ' || c == '\b' || c == '\n' || c == '\t' || c == ' ') + return 1; + return 0; +} + +PUBLIC char * +getword(char *str) +{ + int i; + static char word[MAX_WORD_SIZE]; + + i = 0; + + while (*str && !iswhitespace(*str)) { + word[i] = *str; + str++; + i++; + + if (i == MAX_WORD_SIZE) { + i = (i - 1); + break; + } + } + + word[i] = '\0'; + return word; +} + +/* + * Returns a pointer to the first whitespace in the argument. + */ +PUBLIC char * +eatword(char *str) +{ + while (*str && !iswhitespace(*str)) + str++; + return str; +} + +/* + * Returns a pointer to the first non-whitespace char in the argument. + */ +PUBLIC char * +eatwhite(char *str) +{ + while (*str && iswhitespace(*str)) + str++; + return str; +} + +/* + * Returns a pointer to the same string with trailing spaces removed. + */ +PUBLIC char * +eattailwhite(char *str) +{ + int len; + + if (str == NULL) + return NULL; + + len = strlen(str); + + while (len > 0 && iswhitespace(str[len - 1])) + len--; + + str[len] = '\0'; + return str; +} + +/* + * Returns the next word in a given string. + */ +PUBLIC char * +nextword(char *str) +{ + return eatwhite(eatword(str)); +} + +PUBLIC int +mail_string_to_address(char *addr, char *subj, char *str) +{ + FILE *fp; + char com[1000]; + +#ifdef SENDMAILPROG + snprintf(com, sizeof com, "%s\n", SENDMAILPROG); +#else + snprintf(com, sizeof com, "%s -s \"%s\" %s", MAILPROGRAM, subj, addr); +#endif + + fp = popen(com, "w"); + if (!fp) + return -1; + +#ifdef SENDMAILPROG + fprintf(fp, "To: %s\nSubject: %s\n%s", addr, subj, str); +#else + fprintf(fp, "%s", str); +#endif + + pclose(fp); + return 0; +} + +PUBLIC int +mail_string_to_user(int p, char *subj, char *str) +{ + if (parray[p].emailAddress && + parray[p].emailAddress[0] && + safestring(parray[p].emailAddress)) { + return mail_string_to_address(parray[p].emailAddress, subj, + str); + } else { + return -1; + } +} + +PUBLIC int +mail_file_to_address(char *addr, char *subj, char *fname) +{ + FILE *fp1, *fp2; + char com[1000] = { '\0' }; + char tmp[MAX_LINE_SIZE] = { '\0' }; + + /* maybe unused */ + (void) fp2; + (void) tmp; + +#ifdef SENDMAILPROG + snprintf(com, sizeof com, "%s\n", SENDMAILPROG); +#else + snprintf(com, sizeof com, "%s -s \"%s\" %s < %s&", MAILPROGRAM, subj, + addr, fname); +#endif + if ((fp1 = popen(com, "w")) == NULL) + return -1; +#ifdef SENDMAILPROG + if ((fp2 = fopen(fname, "r")) == NULL) { + pclose(fp1); + return -1; + } + + fprintf(fp1, "To: %s\nSubject: %s\n", addr, subj); + + while (fgets(tmp, sizeof tmp, fp2) != NULL && !feof(fp2)) + fputs(tmp, fp1); + fclose(fp2); +#endif + pclose(fp1); + return 0; +} + +PUBLIC int +mail_file_to_user(int p, char *subj, char *fname) +{ + if (parray[p].emailAddress && + parray[p].emailAddress[0] && + safestring(parray[p].emailAddress)) { + return mail_file_to_address(parray[p].emailAddress, subj, + fname); + } else { + return -1; + } +} + +/* + * Process a command for a user + */ +PUBLIC int +pcommand(int p, char *comstr, ...) +{ + char tmp[MAX_LINE_SIZE] = { '\0' }; + int current_socket = parray[p].socket; + int retval; + va_list ap; + + va_start(ap, comstr); + vsnprintf(tmp, sizeof tmp, comstr, ap); + va_end(ap); + + retval = process_input(current_socket, tmp); + + if (retval == COM_LOGOUT) { + process_disconnection(current_socket); + net_close_connection(current_socket); + } + + return retval; +} + +PUBLIC void +pprintf(int p, const char *format, ...) +{ + char tmp[10 * MAX_LINE_SIZE] = { '\0' }; + int retval; + va_list ap; + + va_start(ap, format); + retval = vsnprintf(tmp, sizeof tmp, format, ap); + va_end(ap); + + UNUSED_VAR(retval); + net_send_string(parray[p].socket, tmp, 1); +} + +PRIVATE void +pprintf_dohightlight(int p) +{ + if (parray[p].highlight & 0x01) + pprintf(p, "\033[7m"); + if (parray[p].highlight & 0x02) + pprintf(p, "\033[1m"); + if (parray[p].highlight & 0x04) + pprintf(p, "\033[4m"); + if (parray[p].highlight & 0x08) + pprintf(p, "\033[2m"); +} + +PUBLIC int +pprintf_highlight(int p, char *format, ...) +{ + char tmp[10 * MAX_LINE_SIZE]; + int retval; + va_list ap; + + pprintf_dohightlight(p); + + va_start(ap, format); + retval = vsnprintf(tmp, sizeof tmp, format, ap); + va_end(ap); + + net_send_string(parray[p].socket, tmp, 1); + + if (parray[p].highlight) + pprintf(p, "\033[0m"); + return retval; +} + +PRIVATE void +sprintf_dohightlight(int p, char *s, size_t size) +{ + if (parray[p].highlight & 0x01) + strlcat(s, "\033[7m", size); + if (parray[p].highlight & 0x02) + strlcat(s, "\033[1m", size); + if (parray[p].highlight & 0x04) + strlcat(s, "\033[4m", size); + if (parray[p].highlight & 0x08) + strlcat(s, "\033[2m", size); +} + +PUBLIC int +psprintf_highlight(int p, char *s, size_t size, char *format, ...) +{ + int retval; + va_list ap; + + if (parray[p].highlight) { + char tmp[1000] = { '\0' }; + + va_start(ap, format); + retval = vsnprintf(tmp, sizeof tmp, format, ap); + va_end(ap); + + sprintf_dohightlight(p, s, size); + strlcat(s, tmp, size); + strlcat(s, "\033[0m", size); + } else { + va_start(ap, format); + retval = vsnprintf(s, size, format, ap); + va_end(ap); + } + + return retval; +} + +PUBLIC int +pprintf_prompt(int p, char *format, ...) +{ + char tmp[10 * MAX_LINE_SIZE]; + int retval; + va_list ap; + + va_start(ap, format); + retval = vsnprintf(tmp, sizeof tmp, format, ap); + va_end(ap); + + net_send_string(parray[p].socket, tmp, 1); + net_send_string(parray[p].socket, parray[p].prompt, 1); + return retval; +} + +PUBLIC int +pprintf_noformat(int p, char *format, ...) +{ + char tmp[10 * MAX_LINE_SIZE]; + int retval; + va_list ap; + + va_start(ap, format); + retval = vsnprintf(tmp, sizeof tmp, format, ap); + va_end(ap); + + net_send_string(parray[p].socket, tmp, 0); + return retval; +} + +PUBLIC int +psend_raw_file(int p, const char *dir, const char *file) +{ + FILE *fp; + char fname[MAX_FILENAME_SIZE] = { '\0' }; + char tmp[MAX_LINE_SIZE] = { '\0' }; + int num; + + if (dir) + snprintf(fname, sizeof fname, "%s/%s", dir, file); + else + strlcpy(fname, file, sizeof fname); + + if ((fp = fopen(fname, "r")) == NULL) + return -1; + + while (!feof(fp) && !ferror(fp)) { + if ((num = fread(tmp, sizeof(char), MAX_LINE_SIZE - 1, + fp)) > 0) { + tmp[num] = '\0'; + net_send_string(parray[p].socket, tmp, 1); + } + } + + if (ferror(fp)) { + warnx("%s: %s: the error indicator is set", __func__, fname); + fclose(fp); + return -1; + } + + fclose(fp); + return 0; +} + +PUBLIC int +psend_file(int p, const char *dir, const char *file) +{ + FILE *fp; + char fname[MAX_FILENAME_SIZE] = { '\0' }; + char tmp[MAX_LINE_SIZE] = { '\0' }; + int lcount = (parray[p].d_height - 1); + + if (parray[p].last_file) + rfree(parray[p].last_file); + parray[p].last_file = NULL; + parray[p].last_file_byte = 0L; + + if (dir) + snprintf(fname, sizeof fname, "%s/%s", dir, file); + else + strlcpy(fname, file, sizeof fname); + + if ((fp = fopen(fname, "r")) == NULL) + return -1; + + while (--lcount > 0) { + if (fgets(tmp, sizeof tmp, fp) == NULL) + break; + net_send_string(parray[p].socket, tmp, 1); + } + + if (!feof(fp)) { + if (ferror(fp)) { + warnx("%s: %s: the error indicator is set", __func__, + fname); + fclose(fp); + return -1; + } + parray[p].last_file = xstrdup(fname); + parray[p].last_file_byte = ftell(fp); + pprintf(p, "Type [next] to see next page.\n"); + } + + fclose(fp); + return 0; +} + +/* + * Marsalis added on 8/27/95 so that [next] does not appear in the + * logout process for those that have a short screen height. + */ +PUBLIC int +psend_logoutfile(int p, char *dir, char *file) +{ + FILE *fp; + char fname[MAX_FILENAME_SIZE] = { '\0' }; + char tmp[MAX_LINE_SIZE] = { '\0' }; + + if (parray[p].last_file) + rfree(parray[p].last_file); + parray[p].last_file = NULL; + parray[p].last_file_byte = 0L; + + if (dir) + snprintf(fname, sizeof fname, "%s/%s", dir, file); + else + strlcpy(fname, file, sizeof fname); + + if ((fp = fopen(fname, "r")) == NULL) + return -1; + + while (fgets(tmp, sizeof tmp, fp) != NULL && !feof(fp)) + net_send_string(parray[p].socket, tmp, 1); + + fclose(fp); + return 0; +} + +PUBLIC int +pmore_file(int p) +{ + FILE *fp; + char tmp[MAX_LINE_SIZE] = { '\0' }; + int lcount = (parray[p].d_height - 1); + + if (!parray[p].last_file) { + pprintf(p, "There is no more.\n"); + return -1; + } + + if ((fp = fopen(parray[p].last_file, "r")) == NULL) { + pprintf(p, "File not found!\n"); + return -1; + } else if (fseek(fp, parray[p].last_file_byte, SEEK_SET) == -1) { + pprintf(p, "Unable to set the file position indicator.\n"); + fclose(fp); + return -1; + } + + while (--lcount > 0) { + if (fgets(tmp, sizeof tmp, fp) == NULL) + break; + net_send_string(parray[p].socket, tmp, 1); + } + + if (!feof(fp)) { + if (ferror(fp)) { + warnx("%s: %s: the error indicator is set", __func__, + parray[p].last_file); + fclose(fp); + return -1; + } else if ((parray[p].last_file_byte = ftell(fp)) == -1) { + warn("%s: %s: ftell", __func__, parray[p].last_file); + fclose(fp); + return -1; + } else + pprintf(p, "Type [next] to see next page.\n"); + } else { + rfree(parray[p].last_file); + parray[p].last_file = NULL; + parray[p].last_file_byte = 0L; + } + + fclose(fp); + return 0; +} + +PUBLIC int +psend_command(int p, char *command, char *input) +{ + FILE *fp; + char tmp[MAX_LINE_SIZE]; + int num; + + if (input) + fp = popen(command, "w"); + else + fp = popen(command, "r"); + if (!fp) + return -1; + + if (input) { + fwrite(input, sizeof(char), strlen(input), fp); + } else { + while (!feof(fp)) { + num = fread(tmp, sizeof(char), MAX_LINE_SIZE - 1, fp); + tmp[num] = '\0'; + net_send_string(parray[p].socket, tmp, 1); + } + } + + pclose(fp); + return 0; +} + +PUBLIC char * +stolower(char *str) +{ + if (!str) + return NULL; + for (int i = 0; str[i]; i++) { + if (isupper(str[i])) + str[i] = tolower(str[i]); + } + return str; +} + +PUBLIC int +safechar(int c) +{ + if (c == '>' || c == '!' || c == '&' || c == '*' || c == '?' || + c == '/' || c == '<' || c == '|' || c == '`' || c == '$') + return 0; + return 1; +} + +PUBLIC int +safestring(char *str) +{ + if (!str) + return 1; + for (int i = 0; str[i]; i++) { + if (!safechar(str[i])) + return 0; + } + return 1; +} + +PUBLIC int +alphastring(char *str) +{ + if (!str) + return 1; + for (int i = 0; str[i]; i++) { + if (!isalpha(str[i])) + return 0; + } + return 1; +} + +PUBLIC int +printablestring(char *str) +{ + if (!str) + return 1; + for (int i = 0; str[i]; i++) { + if (!isprint(str[i]) && str[i] != '\t' && str[i] != '\n') + return 0; + } + return 1; +} + +PUBLIC char * +xstrdup(const char *str) +{ + char *out; + + if (str == NULL) + return NULL; + + const size_t size = strlen(str) + 1; + + out = rmalloc(size); + return memcpy(out, str, size); +} + +PUBLIC char * +hms_desc(int t) +{ + int days, hours, mins, secs; + static char tstr[80]; + + days = t / (60 * 60 * 24); + hours = (t % (60 * 60 * 24)) / (60 * 60); + mins = ((t % (60 * 60 * 24)) % (60 * 60)) / 60; + secs = ((t % (60 * 60 * 24)) % (60 * 60)) % 60; + + if (days == 0 && hours == 0 && mins == 0) { + snprintf(tstr, sizeof tstr, "%d sec%s", secs, (secs == 1 ? "" : + "s")); + } else if (days == 0 && hours == 0) { + snprintf(tstr, sizeof tstr, "%d min%s", mins, (mins == 1 ? "" : + "s")); + } else if (days == 0) { + snprintf(tstr, sizeof tstr, "%d hr%s, %d min%s, %d sec%s", + hours, (hours == 1 ? "" : "s"), + mins, (mins == 1 ? "" : "s"), + secs, (secs == 1 ? "" : "s")); + } else { + snprintf(tstr, sizeof tstr, "%d day%s, %d hour%s, %d minute%s " + "and %d second%s", + days, (days == 1 ? "" : "s"), + hours, (hours == 1 ? "" : "s"), + mins, (mins == 1 ? "" : "s"), + secs, (secs == 1 ? "" : "s")); + } + + return tstr; +} + +PUBLIC char * +hms(int t, int showhour, int showseconds, int spaces) +{ + char tmp[10]; + int h, m, s; + static char tstr[20]; + + h = (t / 3600); + t = (t % 3600); + m = (t / 60); + s = (t % 60); + + if (h || showhour) { + if (spaces) + snprintf(tstr, sizeof tstr, "%d : %02d", h, m); + else + snprintf(tstr, sizeof tstr, "%d:%02d", h, m); + } else { + snprintf(tstr, sizeof tstr, "%d", m); + } + if (showseconds) { + if (spaces) + snprintf(tmp, sizeof tmp, " : %02d", s); + else + snprintf(tmp, sizeof tmp, ":%02d", s); + strlcat(tstr, tmp, sizeof tstr); + } + return tstr; +} + +PRIVATE char * +strtime(struct tm * stm) +{ + static char tstr[100]; +#if defined (SGI) + strftime(tstr, sizeof(tstr), "%a %b %e, %H:%M %Z", stm); +#else + strftime(tstr, sizeof(tstr), "%a %b %e, %k:%M %Z", stm); +#endif + return (tstr); +} + +PUBLIC char * +fix_time(char *old_time) +{ + char date[5] = { '\0' }; + char day[5] = { '\0' }; + char i; + char month[5] = { '\0' }; + static char new_time[20]; + + _Static_assert(4 < ARRAY_SIZE(day), "'day' too small"); + _Static_assert(4 < ARRAY_SIZE(month), "'month' too small"); + _Static_assert(4 < ARRAY_SIZE(date), "'date' too small"); + + if (sscanf(old_time, "%4s %4s %4s", day, month, date) != 3) + warnx("%s: sscanf: too few items (%s)", __func__, old_time); + + if (date[2] != ',') { + i = date[0]; + date[0] = '0'; + date[1] = i; + } + date[2] = '\0'; + + snprintf(new_time, sizeof new_time, "%s, %s %s", day, month, date); + + return &new_time[0]; +} + +PUBLIC char * +strltime(time_t *p_clock) +{ + struct tm stm = {0}; + + errno = 0; + + if (localtime_r(p_clock, &stm) == NULL) { + warn("%s: localtime_r", __func__); + memset(&stm, 0, sizeof stm); + } + return strtime(&stm); +} + +PUBLIC char * +strgtime(time_t *p_clock) +{ + struct tm stm = {0}; + + errno = 0; + + if (gmtime_r(p_clock, &stm) == NULL) { + warn("%s: gmtime_r", __func__); + memset(&stm, 0, sizeof stm); + } + return strtime(&stm); +} + +/* + * This is used only for relative timing since it reports seconds + * since about 5:00 pm on Feb 16, 1994. + */ +PUBLIC unsigned int +tenth_secs(void) +{ + struct timeval tp; + struct timezone tzp; + + gettimeofday(&tp, &tzp); + + return ((tp.tv_sec - 331939277) * 10L) + (tp.tv_usec / 100000); +} + +/* + * This is to translate tenths-secs time back into 1/1/70 time in full + * seconds, because vek didn't read utils.c when he programmed new + * ratings. 1 sec since 1970 fits into a 32 bit int OK. + */ +/* + * 2024-11-23 maxxe: changed the return type to 'time_t' + */ +PUBLIC time_t +untenths(uint64_t tenths) +{ + return (tenths / 10 + 331939277 + 0xffffffff / 10 + 1); +} + +PUBLIC char * +tenth_str(unsigned int t, int spaces) +{ + return hms((t + 5) / 10, 0, 1, spaces); +} + +/* + * XXX: if lines in the file are greater than 1024 bytes in length, + * this won't work! + */ +PUBLIC int +truncate_file(char *file, int lines) +{ +#define MAX_TRUNC_SIZE 100 + FILE *fp; + char tBuf[MAX_TRUNC_SIZE][MAX_LINE_SIZE]; + int bptr = 0, trunc = 0, i; + + if (lines > MAX_TRUNC_SIZE) + lines = MAX_TRUNC_SIZE; + + if ((fp = fopen(file, "r")) == NULL) + return 1; + + while (fgets(tBuf[bptr], MAX_LINE_SIZE, fp) != NULL) { + if (strchr(tBuf[bptr], '\n') == NULL) { + // Line too long + fclose(fp); + return -1; + } + + if (++bptr == lines) { + trunc = 1; + bptr = 0; + } + } + + fclose(fp); + + if (trunc) { + int fd; + + errno = 0; + fd = open(file, g_open_flags[1], g_open_modes); + + if (fd < 0) { + warn("%s: open", __func__); + return 1; + } else if ((fp = fdopen(fd, "w")) == NULL) { + warn("%s: fdopen", __func__); + close(fd); + return 1; + } + + for (i = 0; i < lines; i++) { + fputs(tBuf[bptr], fp); + + if (++bptr == lines) + bptr = 0; + } + + fclose(fp); + } + + return 0; +} + +/* + * XXX: If lines in the file are greater than 1024 bytes in length, + * this won't work! + */ +PUBLIC int +lines_file(char *file) +{ + FILE *fp; + char tmp[MAX_LINE_SIZE]; + int lcount = 0; + + if ((fp = fopen(file, "r")) == NULL) + return 0; + + while (fgets(tmp, sizeof tmp, fp) != NULL && !feof(fp)) + lcount++; + + fclose(fp); + return lcount; +} + +PUBLIC int +file_has_pname(char *fname, char *plogin) +{ + if (!strcmp(file_wplayer(fname), plogin)) + return 1; + if (!strcmp(file_bplayer(fname), plogin)) + return 1; + return 0; +} + +PUBLIC char * +file_wplayer(char *fname) +{ + char *ptr; + static char tmp[MAX_FILENAME_SIZE]; + + strlcpy(tmp, fname, sizeof tmp); + + if ((ptr = strrchr(tmp, '-')) == NULL) + return ""; + *ptr = '\0'; + return tmp; +} + +PUBLIC char * +file_bplayer(char *fname) +{ + char *ptr; + + if ((ptr = strrchr(fname, '-')) == NULL) + return ""; + return ptr + 1; +} + +/* + * Hey, leave this code alone. 'a' is always in network byte order, + * which is big-endian, even on a little-endian machine. I fixed this + * code to handle that correctly a while ago and someone gratuitously + * changed it so that it would work correctly only on a big-endian + * machine (like a Sun). I have now changed it back. --mann + */ +PUBLIC char * +dotQuad(unsigned int a) +{ + static char tmp[20]; + unsigned char *aa = (unsigned char *) &a; + + snprintf(tmp, sizeof tmp, "%d.%d.%d.%d", aa[0], aa[1], aa[2], aa[3]); + return tmp; +} + +PUBLIC int +available_space(void) +{ + return 100000000; /* Infinite space */ +} + +PUBLIC int +file_exists(char *fname) +{ + FILE *fp; + + if ((fp = fopen(fname, "r")) == NULL) + return 0; + fclose(fp); + return 1; +} + +PUBLIC char * +ratstr(int rat) +{ + static char tmp[20][10]; + static int on = 0; + + if (on == 20) + on = 0; + if (rat) { + snprintf(tmp[on], sizeof tmp[on], "%4d", rat); + } else { + snprintf(tmp[on], sizeof tmp[on], "----"); + } + + on++; + return tmp[on - 1]; +} + +PUBLIC char * +ratstrii(int rat, int reg) +{ + static char tmp[20][10]; + static int on = 0; + + if (on == 20) + on = 0; + if (rat) { + snprintf(tmp[on], sizeof tmp[on], "%4d", rat); + } else { + if (reg) { + snprintf(tmp[on], sizeof tmp[on], "----"); + } else { + snprintf(tmp[on], sizeof tmp[on], "++++"); + } + } + + on++; + return tmp[on - 1]; +} + +/* + * Fill 't_buffer' with anything matching "want*" in file tree + */ +PRIVATE void +t_sft(const char *want, struct t_tree *t) +{ + if (t) { + const char *v_want = (want ? want : ""); + int cmp = strncmp(v_want, t->name, strlen(v_want)); + + if (cmp <= 0) // If 'want' <= this one, look left + t_sft(want, t->left); + + if (t_buffersize && cmp == 0) { // If a match, add it to buffer + t_buffersize--; + *t_buffer++ = t->name; + } + + if (cmp >= 0) // If 'want' >= this one, look right + t_sft(want, t->right); + } +} + +/* + * Delete file tree + */ +PRIVATE void +t_cft(struct t_tree **t) +{ + if (t != NULL && *t != NULL) { + t_cft(&(*t)->left); + t_cft(&(*t)->right); + rfree((*t)->name); + rfree(*t); + *t = NULL; + } +} + +/* + * Make file tree for dir 'd' + */ +PRIVATE void +t_mft(struct t_dirs *d) +{ + DIR *dirp; +#ifdef USE_DIRENT + struct dirent *dp; +#else + struct direct *dp; +#endif + struct t_tree **t; + + if ((dirp = opendir(d->name)) == NULL) { + fprintf(stderr, "FICS: %s: couldn't opendir\n", __func__); + return; + } + while ((dp = readdir(dirp))) { + t = &d->files; + + if (dp->d_name[0] != '.') { // skip anything starting with '.' + size_t size; + + while (*t) { + if (strcmp(dp->d_name, (*t)->name) < 0) + t = &(*t)->left; + else + t = &(*t)->right; + } + + size = strlen(dp->d_name) + 1; + *t = rmalloc(sizeof(struct t_tree)); + (*t)->right = (*t)->left = NULL; + (*t)->name = rmalloc(size); + + if (strlcpy((*t)->name, dp->d_name, size) >= size) + errx(1, "%s: name too long", __func__); + } + } + closedir(dirp); +} + +/* + * dir = directory to search + * filter = what to search for + * buffer = where to store pointers to matches + * buffersize = how many pointers will fit inside buffer + */ +PUBLIC int +search_directory(char *dir, char *filter, char **buffer, int buffersize) +{ + int cmp; + static struct t_dirs *ramdirs = NULL; + struct stat statbuf; + struct t_dirs** i; + + t_buffer = buffer; + t_buffersize = buffersize; + + if (!stat(dir, &statbuf)) { + i = &ramdirs; + + while (*i) { // Find dir in dir tree + if ((cmp = strcmp(dir, (*i)->name)) == 0) + break; + else if (cmp < 0) + i = &(*i)->left; + else + i = &(*i)->right; + } + + if (!*i) { // If dir isn't in dir tree, + size_t size; // add him. + + size = strlen(dir) + 1; + *i = rmalloc(sizeof(struct t_dirs)); + (*i)->left = (*i)->right = NULL; + (*i)->files = NULL; + (*i)->name = rmalloc(size); + + if (strlcpy((*i)->name, dir, size) >= size) + errx(1, "%s: dir too long", __func__); + } + + if ((*i)->files) { // Delete any obsolete file + // tree. + if ((*i)->mtime != statbuf.st_mtime) + t_cft(&(*i)->files); + } + + if ((*i)->files == NULL) { // If no file tree for him, + // make one. + (*i)->mtime = statbuf.st_mtime; + t_mft(*i); + } + + t_sft(filter, (*i)->files); // Finally, search for matches. + } + + return (buffersize - t_buffersize); +} + +PUBLIC int +display_directory(int p, char **buffer, int count) +{ // 'buffer' contains 'count' string pointers. + int i; + multicol *m = multicol_start(count); + + for (i = 0; (i < count); i++) + multicol_store(m, *buffer++); + multicol_pprint(m, p, 78, 1); + multicol_end(m); + return (i); +} |