home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
Usenet 1994 October
/
usenetsourcesnewsgroupsinfomagicoctober1994disk2.iso
/
unix
/
volume27
/
trn-3.3
/
part05
/
rt-util.c
< prev
next >
Wrap
C/C++ Source or Header
|
1993-11-27
|
13KB
|
583 lines
/* $Id: rt-util.c,v 3.0 1992/12/14 00:14:12 davison Trn $
*/
/* The authors make no claims as to the fitness or correctness of this software
* for any use whatsoever, and it is provided as is. Any use of this software
* is at the user's own risk.
*/
#include "EXTERN.h"
#include "common.h"
#include "cache.h"
#include "ngdata.h"
#include "artio.h"
#include "rthread.h"
#include "rt-select.h"
#include "term.h"
#include "INTERN.h"
#include "rt-util.h"
/* Name-munging routines written by Ross Ridge.
** Enhanced by Wayne Davison.
*/
/* Extract the full-name part of an email address, returning NULL if not
** found.
*/
char *
extract_name(name)
char *name;
{
char *s;
char *lparen, *rparen;
char *langle;
while (isspace(*name)) {
name++;
}
lparen = index(name, '(');
rparen = rindex(name, ')');
langle = index(name, '<');
if (!lparen && !langle) {
return NULL;
} else
if (langle && (!lparen || !rparen || lparen > langle || rparen < langle)) {
if (langle == name) {
return NULL;
}
*langle = '\0';
} else {
name = lparen;
*name++ = '\0';
while (isspace(*name)) {
name++;
}
if (name == rparen) {
return NULL;
}
if (rparen != NULL) {
*rparen = '\0';
}
}
if (*name == '"') {
name++;
while (isspace(*name)) {
name++;
}
if ((s = rindex(name, '"')) != NULL) {
*s = '\0';
}
}
return name;
}
/* If necessary, compress a net user's full name by playing games with
** initials and the middle name(s). If we start with "Ross Douglas Ridge"
** we try "Ross D Ridge", "Ross Ridge", "R D Ridge" and finally "R Ridge"
** before simply truncating the thing. We also turn "R. Douglas Ridge"
** into "Douglas Ridge" and "Ross Ridge D.D.S." into "Ross Ridge" as a
** first step of the compaction, if needed.
*/
char *
compress_name(name, max)
char *name;
int max;
{
register char *s, *last, *mid, *d;
register int len, namelen, midlen;
int notlast;
/* First remove white space from both ends. */
while (isspace(*name)) {
name++;
}
if ((len = strlen(name)) == 0) {
return name;
}
s = name + len - 1;
while (isspace(*s)) {
s--;
}
s[1] = '\0';
if (s - name + 1 <= max) {
return name;
}
/* Look for characters that likely mean the end of the name
** and the start of some hopefully uninteresting additional info.
** Spliting at a comma is somewhat questionalble, but since
** "Ross Ridge, The Great HTMU" comes up much more often than
** "Ridge, Ross" and since "R HTMU" is worse than "Ridge" we do
** it anyways.
*/
for (d = name + 1; *d; d++) {
if (*d == ',' || *d == ';' || *d == '(' || *d == '@'
|| (*d == '-' && (d[1] == '-' || d[1] == ' '))) {
*d-- = '\0';
s = d;
break;
}
}
/* Find the last name */
do {
notlast = 0;
while (isspace(*s)) {
s--;
}
s[1] = '\0';
len = s - name + 1;
if (len <= max) {
return name;
}
/* If the last name is an abbreviation it's not the one we want. */
if (*s == '.')
notlast = 1;
while (!isspace(*s)) {
if (s == name) { /* only one name */
name[max] = '\0';
return name;
}
if (isdigit(*s)) { /* probably a phone number */
notlast = 1; /* so chuck it */
}
s--;
}
} while (notlast);
last = s-- + 1;
/* Look for a middle name */
while (isspace(*s)) { /* get rid of any extra space */
len--;
s--;
}
mid = name;
while (!isspace(*mid)) {
mid++;
}
namelen = mid - name + 1;
if (mid == s+1) { /* no middle name */
mid = 0;
} else {
*mid++ = '\0';
while (isspace(*mid)) {
len--;
mid++;
}
midlen = s - mid + 2;
/* If first name is an initial and middle isn't and it all fits
** without the first initial, drop it. */
if (len > max && mid != s && mid[1] != '.'
&& (!name[1] || (name[1] == '.' && !name[2]))
&& len - namelen <= max) {
len -= namelen;
name = mid;
mid = 0;
}
}
s[1] = '\0';
if (mid && len > max) {
/* Turn middle names into intials */
len -= s - mid + 2;
d = s = mid;
while (*s) {
if (isalpha(*s)) {
if (d != mid) {
*d++ = ' ';
}
*d++ = *s++;
}
while (*s && !isspace(*s)) {
s++;
}
while (isspace(*s)) {
s++;
}
}
if (d != mid) {
*d = '\0';
midlen = d - mid + 1;
len += midlen;
} else {
mid = 0;
}
}
if (len > max) {
/* If the first name fits without the middle initials, drop them */
if (mid && len - midlen <= max) {
len -= midlen;
mid = 0;
} else {
/* Turn the first name into an initial */
len -= namelen - 2;
name[1] = '\0';
namelen = 2;
if (len > max) {
/* Dump the middle initials (if present) */
if (mid) {
len -= midlen;
mid = 0;
}
if (len > max) {
/* Finally just truncate the last name */
last[max - 2] = '\0';
}
}
}
}
/* Paste the names back together */
d = name + namelen;
if (mid) {
d[-1] = ' ';
strcpy(d, mid);
d += midlen;
}
d[-1] = ' ';
strcpy(d, last);
return name;
}
/* Compress an email address, trying to keep as much of the local part of
** the addresses as possible. The order of precence is @ ! %, but
** @ % ! may be better...
*/
static char *
compress_address(name, max)
char *name;
int max;
{
char *s, *at, *bang, *hack, *start;
int len;
/* Remove white space from both ends. */
while (isspace(*name)) {
name++;
}
if ((len = strlen(name)) == 0) {
return name;
}
s = name + len - 1;
while (isspace(*s)) {
s--;
}
s[1] = '\0';
if (*name == '<') {
name++;
if (*s == '>') {
*s-- = '\0';
}
}
if ((len = s - name + 1) <= max) {
return name;
}
at = bang = hack = NULL;
for (s = name + 1; *s; s++) {
/* If there's whitespace in the middle then it's probably not
** really an email address. */
if (isspace(*s)) {
name[max] = '\0';
return name;
}
switch (*s) {
case '@':
if (at == NULL) {
at = s;
}
break;
case '!':
if (at == NULL) {
bang = s;
hack = NULL;
}
break;
case '%':
if (at == NULL && hack == NULL) {
hack = s;
}
break;
}
}
if (at == NULL) {
at = name + len;
}
if (hack != NULL) {
if (bang != NULL) {
if (at - bang - 1 >= max) {
start = bang + 1;
} else if (at - name >= max) {
start = at - max;
} else {
start = name;
}
} else {
start = name;
}
} else if (bang != NULL) {
if (at - name >= max) {
start = at - max;
} else {
start = name;
}
} else {
start = name;
}
if (len - (start - name) > max) {
start[max] = '\0';
}
return start;
}
/* Fit the author name in <max> chars. Uses the comment portion if present
** and pads with spaces.
*/
char *
compress_from(ap, size)
ARTICLE *ap;
int size;
{
char *s, *t;
int len;
for (t = cmd_buf, s = ap && ap->from? ap->from : nullstr; *s; ) {
if ((unsigned char)*s < ' ')
*t++ = ' ', s++;
else
*t++ = *s++;
}
*t = '\0';
if ((s = extract_name(cmd_buf)) != NULL)
s = compress_name(s, size);
else
s = compress_address(cmd_buf, size);
len = strlen(s);
if (!len) {
strcpy(s,"NO NAME");
len = 7;
}
while (len < size)
s[len++] = ' ';
s[size] = '\0';
return s;
}
#define EQ(x,y) ((isupper(x) ? tolower(x) : (x)) == (y))
/* Parse the subject to skip past any "Re[:^]"s at the start.
*/
char *
get_subject_start(str)
register char *str;
{
while (*str && (unsigned char)*str <= ' ')
str++;
while (EQ(str[0], 'r') && EQ(str[1], 'e')) { /* check for Re: */
register char *cp = str + 2;
if (*cp == '^') { /* allow Re^2: */
while (*++cp <= '9' && *cp >= '0')
;
}
if (*cp != ':')
break;
while (*++cp == ' ')
;
str = cp;
}
return str;
}
/* Output a subject in <max> chars. Does intelligent trimming that tries to
** save the last two words on the line, excluding "(was: blah)" if needed.
*/
char *
compress_subj(ap, max)
ARTICLE *ap;
int max;
{
register char *cp;
register int len;
ARTICLE *first;
if (!ap)
return "<MISSING>";
/* Put a preceeding '>' on subjects that are replies to other articles */
cp = buf;
first = (ThreadedGroup? ap->subj->thread : ap->subj->articles);
if (ap != first || (ap->flags & AF_HAS_RE)
|| !(!(ap->flags&AF_READ) ^ sel_rereading))
*cp++ = '>';
strcpy(cp, ap->subj->str + 4);
/* Remove "(was: oldsubject)", because we already know the old subjects.
** Also match "(Re: oldsubject)". Allow possible spaces after the ('s.
*/
for (cp = buf; (cp = index(cp+1, '(')) != Nullch;) {
while (*++cp == ' ')
;
if (EQ(cp[0], 'w') && EQ(cp[1], 'a') && EQ(cp[2], 's')
&& (cp[3] == ':' || cp[3] == ' ')) {
*--cp = '\0';
break;
}
if (EQ(cp[0], 'r') && EQ(cp[1], 'e')
&& ((cp[2]==':' && cp[3]==' ') || (cp[2]=='^' && cp[4]==':'))) {
*--cp = '\0';
break;
}
}
len = strlen(buf);
if (!unbroken_subjects && len > max) {
char *last_word;
/* Try to include the last two words on the line while trimming */
if ((last_word = rindex(buf, ' ')) != Nullch) {
char *next_to_last;
*last_word = '\0';
if ((next_to_last = rindex(buf, ' ')) != Nullch) {
if (next_to_last-buf >= len - max + 3 + 10-1)
cp = next_to_last;
else
cp = last_word;
} else
cp = last_word;
*last_word = ' ';
if (cp-buf >= len - max + 3 + 10-1) {
sprintf(buf + max - (len-(cp-buf)+3), "...%s", cp + 1);
len = max;
}
}
}
if (len > max)
buf[max] = '\0';
return buf;
}
#ifndef HAS_STRCASECMP
static unsigned char casemap[256] = {
0x00,0x01,0x02,0x03,0x04,0x05,0x06,0x07,
0x08,0x09,0x0A,0x0B,0x0C,0x0D,0x0E,0x0F,
0x10,0x11,0x12,0x13,0x14,0x15,0x16,0x17,
0x18,0x19,0x1A,0x1B,0x1C,0x1D,0x1E,0x1F,
0x20,0x21,0x22,0x23,0x24,0x25,0x26,0x27,
0x28,0x29,0x2A,0x2B,0x2C,0x2D,0x2E,0x2F,
0x30,0x31,0x32,0x33,0x34,0x35,0x36,0x37,
0x38,0x39,0x3A,0x3B,0x3C,0x3D,0x3E,0x3F,
0x40,0x61,0x62,0x63,0x64,0x65,0x66,0x67,
0x68,0x69,0x6A,0x6B,0x6C,0x6D,0x6E,0x6F,
0x70,0x71,0x72,0x73,0x74,0x75,0x76,0x77,
0x78,0x79,0x7A,0x7B,0x5C,0x5D,0x5E,0x5F,
0x60,0x61,0x62,0x63,0x64,0x65,0x66,0x67,
0x68,0x69,0x6A,0x6B,0x6C,0x6D,0x6E,0x6F,
0x70,0x71,0x72,0x73,0x74,0x75,0x76,0x77,
0x78,0x79,0x7A,0x7B,0x7C,0x7D,0x7E,0x7F,
0x80,0x81,0x82,0x83,0x84,0x85,0x86,0x87,
0x88,0x89,0x8A,0x8B,0x8C,0x8D,0x8E,0x8F,
0x90,0x91,0x92,0x93,0x94,0x95,0x96,0x97,
0x98,0x99,0x9A,0x9B,0x9C,0x9D,0x9E,0x9F,
0xA0,0xA1,0xA2,0xA3,0xA4,0xA5,0xA6,0xA7,
0xA8,0xA9,0xAA,0xAB,0xAC,0xAD,0xAE,0xAF,
0xB0,0xB1,0xB2,0xB3,0xB4,0xB5,0xB6,0xB7,
0xB8,0xB9,0xBA,0xBB,0xBC,0xBD,0xBE,0xBF,
0xC0,0xC1,0xC2,0xC3,0xC4,0xC5,0xC6,0xC7,
0xC8,0xC9,0xCA,0xCB,0xCC,0xCD,0xCE,0xCF,
0xD0,0xD1,0xD2,0xD3,0xD4,0xD5,0xD6,0xD7,
0xD8,0xD9,0xDA,0xDB,0xDC,0xDD,0xDE,0xDF,
0xE0,0xE1,0xE2,0xE3,0xE4,0xE5,0xE6,0xE7,
0xE8,0xE9,0xEA,0xEB,0xEC,0xED,0xEE,0xEF,
0xF0,0xF1,0xF2,0xF3,0xF4,0xF5,0xF6,0xF7,
0xF8,0xF9,0xFA,0xFB,0xFC,0xFD,0xFE,0xFF
};
int
strcasecmp(s1, s2)
register char *s1, *s2;
{
do {
if (casemap[(unsigned)*s1++] != casemap[(unsigned)*s2])
return casemap[(unsigned)s1[-1]] - casemap[(unsigned)*s2];
} while (*s2++ != '\0');
return 0;
}
int
strncasecmp(s1, s2, len)
register char *s1, *s2;
register int len;
{
while (len--) {
if (casemap[(unsigned)*s1++] != casemap[(unsigned)*s2])
return casemap[(unsigned)s1[-1]] - casemap[(unsigned)*s2];
if (*s2++ == '\0')
break;
}
return 0;
}
#endif
/* Modified version of a spinner originally found in Clifford Adams' strn. */
static char spinchars[] = {'|','/','-','\\'};
static int spin_place; /* represents place in spinchars array */
static int spin_count; /* counter for when to spin */
static int spin_level INIT(0); /* used to allow non-interfering nested spins */
static int spin_mode;
static ART_NUM spin_art;
static ART_POS spin_tell;
void
setspin(mode)
int mode;
{
switch (mode) {
case SPIN_FOREGROUND:
case SPIN_BACKGROUND:
if (!spin_level++) {
if ((spin_art = openart) != 0)
spin_tell = ftell(artfp);
spin_count = 1; /* not 0 to prevent immediate spin display */
spin_place = 1; /* start with slash... */
}
spin_mode = mode;
break;
case SPIN_POP:
if (--spin_level > 0)
break;
/* FALL THROUGH */
case SPIN_OFF:
spin_level = 0;
if (spin_place > 1) { /* we have spun at least once */
putchar(spin_char); /* get rid of spin character */
backspace();
fflush(stdout);
spin_place = 0;
}
if (spin_art) {
artopen(spin_art);
fseek(artfp,spin_tell,0); /* do not screw up the pager */
spin_art = 0;
}
break;
}
}
void
spin(count)
int count; /* modulus for the spin... */
{
if (!spin_level || (!bkgnd_spinner && spin_mode == SPIN_BACKGROUND))
return;
if (!(spin_count++%count)) {
if (spin_mode == SPIN_FOREGROUND)
putchar('.');
else {
putchar(spinchars[spin_place++%4]);
backspace();
}
fflush(stdout);
}
}