1505 lines
28 KiB
C
1505 lines
28 KiB
C
#include <cdk_int.h>
|
|
|
|
/*
|
|
* $Author: tom $
|
|
* $Date: 2016/12/04 19:33:34 $
|
|
* $Revision: 1.219 $
|
|
*/
|
|
|
|
#define L_MARKER '<'
|
|
#define R_MARKER '>'
|
|
|
|
char *GPasteBuffer = 0;
|
|
|
|
/*
|
|
* This beeps then flushes the stdout stream.
|
|
*/
|
|
void Beep (void)
|
|
{
|
|
beep ();
|
|
fflush (stdout);
|
|
}
|
|
|
|
/*
|
|
* This sets a string to the given character.
|
|
*/
|
|
void cleanChar (char *s, int len, char character)
|
|
{
|
|
if (s != 0)
|
|
{
|
|
int x;
|
|
for (x = 0; x < len; x++)
|
|
{
|
|
s[x] = character;
|
|
}
|
|
s[--x] = '\0';
|
|
}
|
|
}
|
|
|
|
void cleanChtype (chtype *s, int len, chtype character)
|
|
{
|
|
if (s != 0)
|
|
{
|
|
int x;
|
|
for (x = 0; x < len; x++)
|
|
{
|
|
s[x] = character;
|
|
}
|
|
s[--x] = '\0';
|
|
}
|
|
}
|
|
|
|
/*
|
|
* This takes an x and y position and realigns the values iff they sent in
|
|
* values like CENTER, LEFT, RIGHT, ...
|
|
*/
|
|
void alignxy (WINDOW *window, int *xpos, int *ypos, int boxWidth, int boxHeight)
|
|
{
|
|
int first, gap, last;
|
|
|
|
first = getbegx (window);
|
|
last = getmaxx (window);
|
|
if ((gap = (last - boxWidth)) < 0)
|
|
gap = 0;
|
|
last = first + gap;
|
|
|
|
switch (*xpos)
|
|
{
|
|
case LEFT:
|
|
(*xpos) = first;
|
|
break;
|
|
case RIGHT:
|
|
(*xpos) = first + gap;
|
|
break;
|
|
case CENTER:
|
|
(*xpos) = first + (gap / 2);
|
|
break;
|
|
default:
|
|
if ((*xpos) > last)
|
|
(*xpos) = last;
|
|
else if ((*xpos) < first)
|
|
(*xpos) = first;
|
|
break;
|
|
}
|
|
|
|
first = getbegy (window);
|
|
last = getmaxy (window);
|
|
if ((gap = (last - boxHeight)) < 0)
|
|
gap = 0;
|
|
last = first + gap;
|
|
|
|
switch (*ypos)
|
|
{
|
|
case TOP:
|
|
(*ypos) = first;
|
|
break;
|
|
case BOTTOM:
|
|
(*ypos) = first + gap;
|
|
break;
|
|
case CENTER:
|
|
(*ypos) = first + (gap / 2);
|
|
break;
|
|
default:
|
|
if ((*ypos) > last)
|
|
{
|
|
(*ypos) = last;
|
|
}
|
|
else if ((*ypos) < first)
|
|
{
|
|
(*ypos) = first;
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
|
|
/*
|
|
* This takes a string, a field width and a justification type
|
|
* and returns the adjustment to make, to fill
|
|
* the justification requirement.
|
|
*/
|
|
int justifyString (int boxWidth, int mesgLength, int justify)
|
|
{
|
|
/*
|
|
* Make sure the message isn't longer than the width.
|
|
* If it is, return 0.
|
|
*/
|
|
if (mesgLength >= boxWidth)
|
|
return (0);
|
|
|
|
/* Try to justify the message. */
|
|
if (justify == LEFT)
|
|
return (0);
|
|
|
|
if (justify == RIGHT)
|
|
return boxWidth - mesgLength;
|
|
|
|
if (justify == CENTER)
|
|
return ((int)((boxWidth - mesgLength) / 2));
|
|
|
|
return (justify);
|
|
}
|
|
|
|
/*
|
|
* This frees a string if it is not null. This is a safety
|
|
* measure. Some compilers let you free a null string. I
|
|
* don't like that idea.
|
|
*/
|
|
void freeChar (char *string)
|
|
{
|
|
freeChecked (string);
|
|
}
|
|
|
|
void freeChtype (chtype *string)
|
|
{
|
|
freeChecked (string);
|
|
}
|
|
|
|
/*
|
|
* Corresponding list freeing (does not free the list pointer).
|
|
*/
|
|
void freeCharList (char **list, unsigned size)
|
|
{
|
|
if (list != 0)
|
|
{
|
|
while (size-- != 0)
|
|
{
|
|
freeChar (list[size]);
|
|
list[size] = 0;
|
|
}
|
|
}
|
|
}
|
|
|
|
void freeChtypeList (chtype **list, unsigned size)
|
|
{
|
|
if (list != 0)
|
|
{
|
|
while (size-- != 0)
|
|
{
|
|
freeChtype (list[size]);
|
|
list[size] = 0;
|
|
}
|
|
}
|
|
}
|
|
|
|
/*
|
|
* This performs a safe copy of a string. This means it adds the null
|
|
* terminator on the end of the string, like strdup().
|
|
*/
|
|
char *copyChar (const char *original)
|
|
{
|
|
char *newstring = 0;
|
|
|
|
if (original != 0)
|
|
{
|
|
if ((newstring = typeMallocN (char, strlen (original) + 1)) != 0)
|
|
strcpy (newstring, original);
|
|
}
|
|
return (newstring);
|
|
}
|
|
|
|
chtype *copyChtype (const chtype *original)
|
|
{
|
|
chtype *newstring = 0;
|
|
|
|
if (original != 0)
|
|
{
|
|
int len = chlen (original);
|
|
|
|
if ((newstring = typeMallocN (chtype, len + 4)) != 0)
|
|
{
|
|
int x;
|
|
|
|
for (x = 0; x < len; x++)
|
|
{
|
|
newstring[x] = original[x];
|
|
}
|
|
newstring[len] = '\0';
|
|
newstring[len + 1] = '\0';
|
|
}
|
|
}
|
|
return (newstring);
|
|
}
|
|
|
|
/*
|
|
* Copy the given lists.
|
|
*/
|
|
char **copyCharList (const char **list)
|
|
{
|
|
size_t size = (size_t) lenCharList (list) + 1;
|
|
char **result = typeMallocN (char *, size);
|
|
|
|
if (result != 0)
|
|
{
|
|
unsigned n;
|
|
for (n = 0; n < size; ++n)
|
|
result[n] = copyChar (list[n]);
|
|
}
|
|
return result;
|
|
}
|
|
|
|
chtype **copyChtypeList (const chtype **list)
|
|
{
|
|
size_t size = (size_t) lenChtypeList (list) + 1;
|
|
chtype **result = typeMallocN (chtype *, size);
|
|
|
|
if (result != 0)
|
|
{
|
|
unsigned n;
|
|
for (n = 0; n < size; ++n)
|
|
result[n] = copyChtype (list[n]);
|
|
}
|
|
return result;
|
|
}
|
|
|
|
/*
|
|
* Return the length of the given lists.
|
|
*/
|
|
int lenCharList (const char **list)
|
|
{
|
|
int result = 0;
|
|
if (list != 0)
|
|
{
|
|
while (*list++ != 0)
|
|
++result;
|
|
}
|
|
return result;
|
|
}
|
|
|
|
int lenChtypeList (const chtype **list)
|
|
{
|
|
int result = 0;
|
|
if (list != 0)
|
|
{
|
|
while (*list++ != 0)
|
|
++result;
|
|
}
|
|
return result;
|
|
}
|
|
|
|
/*
|
|
* This reads a file and sticks it into the char *** provided.
|
|
*/
|
|
int CDKreadFile (const char *filename, char ***array)
|
|
{
|
|
FILE *fd;
|
|
char temp[BUFSIZ];
|
|
unsigned lines = 0;
|
|
unsigned used = 0;
|
|
|
|
/* Can we open the file? */
|
|
if ((fd = fopen (filename, "r")) == 0)
|
|
{
|
|
return (-1);
|
|
}
|
|
|
|
while ((fgets (temp, sizeof (temp), fd) != 0))
|
|
{
|
|
size_t len = strlen (temp);
|
|
if (len != 0 && temp[len - 1] == '\n')
|
|
temp[--len] = '\0';
|
|
used = CDKallocStrings (array, temp, lines++, used);
|
|
}
|
|
fclose (fd);
|
|
|
|
return (int)(lines);
|
|
}
|
|
|
|
#define DigitOf(c) ((c)-'0')
|
|
|
|
static int encodeAttribute (const char *string, int from, chtype *mask)
|
|
{
|
|
int pair = 0;
|
|
|
|
*mask = 0;
|
|
switch (string[from + 1])
|
|
{
|
|
case 'B':
|
|
*mask = A_BOLD;
|
|
break;
|
|
case 'D':
|
|
*mask = A_DIM;
|
|
break;
|
|
case 'K':
|
|
*mask = A_BLINK;
|
|
break;
|
|
case 'R':
|
|
*mask = A_REVERSE;
|
|
break;
|
|
case 'S':
|
|
*mask = A_STANDOUT;
|
|
break;
|
|
case 'U':
|
|
*mask = A_UNDERLINE;
|
|
break;
|
|
}
|
|
|
|
if (*mask != 0)
|
|
{
|
|
from++;
|
|
}
|
|
else
|
|
{
|
|
int digits;
|
|
|
|
pair = 0;
|
|
for (digits = 1; digits <= 3; ++digits)
|
|
{
|
|
if (!isdigit (CharOf (string[1 + from])))
|
|
break;
|
|
pair *= 10;
|
|
pair += DigitOf (string[++from]);
|
|
}
|
|
#ifdef HAVE_START_COLOR
|
|
#define MAX_PAIR (int) (A_COLOR / (((~A_COLOR) << 1) & A_COLOR))
|
|
if (pair > MAX_PAIR)
|
|
pair = MAX_PAIR;
|
|
*mask = (chtype)COLOR_PAIR (pair);
|
|
#else
|
|
*mask = A_BOLD;
|
|
#endif
|
|
}
|
|
return from;
|
|
}
|
|
|
|
/*
|
|
* The reverse of encodeAttribute()
|
|
* Well almost. If attributes such as bold and underline are combined in
|
|
* the same string, we do not necessarily reconstruct them in the same order.
|
|
* Also, alignment markers and tabs are lost.
|
|
*/
|
|
static unsigned decodeAttribute (char *string,
|
|
unsigned from,
|
|
chtype oldattr,
|
|
chtype newattr)
|
|
{
|
|
/* *INDENT-OFF* */
|
|
static const struct {
|
|
int code;
|
|
chtype mask;
|
|
} table[] = {
|
|
{ 'B', A_BOLD },
|
|
{ 'D', A_DIM },
|
|
{ 'K', A_BLINK },
|
|
{ 'R', A_REVERSE },
|
|
{ 'S', A_STANDOUT },
|
|
{ 'U', A_UNDERLINE },
|
|
};
|
|
/* *INDENT-ON* */
|
|
|
|
char temp[80];
|
|
char *result = (string != 0) ? string : temp;
|
|
char *base = result;
|
|
chtype tmpattr = oldattr & A_ATTRIBUTES;
|
|
|
|
newattr &= A_ATTRIBUTES;
|
|
if (tmpattr != newattr)
|
|
{
|
|
while (tmpattr != newattr)
|
|
{
|
|
unsigned n;
|
|
bool found = FALSE;
|
|
|
|
for (n = 0; n < sizeof (table) / sizeof (table[0]); ++n)
|
|
{
|
|
if ((table[n].mask & tmpattr) != (table[n].mask & newattr))
|
|
{
|
|
found = TRUE;
|
|
*result++ = L_MARKER;
|
|
if (table[n].mask & tmpattr)
|
|
{
|
|
*result++ = '!';
|
|
tmpattr &= ~(table[n].mask);
|
|
}
|
|
else
|
|
{
|
|
*result++ = '/';
|
|
tmpattr |= (table[n].mask);
|
|
}
|
|
*result++ = (char)table[n].code;
|
|
break;
|
|
}
|
|
}
|
|
#ifdef HAVE_START_COLOR
|
|
if ((tmpattr & A_COLOR) != (newattr & A_COLOR))
|
|
{
|
|
int oldpair = PAIR_NUMBER (tmpattr);
|
|
int newpair = PAIR_NUMBER (newattr);
|
|
if (!found)
|
|
{
|
|
found = TRUE;
|
|
*result++ = L_MARKER;
|
|
}
|
|
if (newpair == 0)
|
|
{
|
|
*result++ = '!';
|
|
sprintf (result, "%d", oldpair);
|
|
}
|
|
else
|
|
{
|
|
*result++ = '/';
|
|
sprintf (result, "%d", newpair);
|
|
}
|
|
result += strlen (result);
|
|
tmpattr &= ~A_COLOR;
|
|
newattr &= ~A_COLOR;
|
|
}
|
|
#endif
|
|
if (found)
|
|
*result++ = R_MARKER;
|
|
else
|
|
break;
|
|
}
|
|
}
|
|
|
|
return (from + (unsigned)(result - base));
|
|
}
|
|
|
|
/*
|
|
* This function takes a character string, full of format markers
|
|
* and translates them into a chtype * array. This is better suited
|
|
* to curses, because curses uses chtype almost exclusively
|
|
*/
|
|
chtype *char2Chtype (const char *string, int *to, int *align)
|
|
{
|
|
chtype *result = 0;
|
|
chtype attrib;
|
|
chtype lastChar;
|
|
chtype mask;
|
|
|
|
(*to) = 0;
|
|
*align = LEFT;
|
|
|
|
if (string != 0 && *string != 0)
|
|
{
|
|
int len = (int)strlen (string);
|
|
int pass;
|
|
int used = 0;
|
|
/*
|
|
* We make two passes because we may have indents and tabs to expand, and
|
|
* do not know in advance how large the result will be.
|
|
*/
|
|
for (pass = 0; pass < 2; pass++)
|
|
{
|
|
int insideMarker;
|
|
int from;
|
|
int adjust;
|
|
int start;
|
|
int x = 3;
|
|
|
|
if (pass != 0)
|
|
{
|
|
if ((result = typeMallocN (chtype, used + 2)) == 0)
|
|
{
|
|
used = 0;
|
|
break;
|
|
}
|
|
}
|
|
adjust = 0;
|
|
attrib = A_NORMAL;
|
|
start = 0;
|
|
used = 0;
|
|
|
|
/* Look for an alignment marker. */
|
|
if (*string == L_MARKER)
|
|
{
|
|
if (string[1] == 'C' && string[2] == R_MARKER)
|
|
{
|
|
(*align) = CENTER;
|
|
start = 3;
|
|
}
|
|
else if (string[1] == 'R' && string[2] == R_MARKER)
|
|
{
|
|
(*align) = RIGHT;
|
|
start = 3;
|
|
}
|
|
else if (string[1] == 'L' && string[2] == R_MARKER)
|
|
{
|
|
start = 3;
|
|
}
|
|
else if (string[1] == 'B' && string[2] == '=')
|
|
{
|
|
/* Set the item index value in the string. */
|
|
if (result != 0)
|
|
{
|
|
result[0] = ' ';
|
|
result[1] = ' ';
|
|
result[2] = ' ';
|
|
}
|
|
|
|
/* Pull out the bullet marker. */
|
|
while (string[x] != R_MARKER && string[x] != 0)
|
|
{
|
|
if (result != 0)
|
|
result[x] = (chtype)string[x] | A_BOLD;
|
|
x++;
|
|
}
|
|
adjust = 1;
|
|
|
|
/* Set the alignment variables. */
|
|
start = x;
|
|
used = x;
|
|
}
|
|
else if (string[1] == 'I' && string[2] == '=')
|
|
{
|
|
from = 2;
|
|
x = 0;
|
|
|
|
while (string[++from] != R_MARKER && string[from] != 0)
|
|
{
|
|
if (isdigit (CharOf (string[from])))
|
|
{
|
|
adjust = (adjust * 10) + DigitOf (string[from]);
|
|
x++;
|
|
}
|
|
}
|
|
|
|
start = x + 4;
|
|
}
|
|
}
|
|
|
|
while (adjust-- > 0)
|
|
{
|
|
if (result != 0)
|
|
result[used] = ' ';
|
|
used++;
|
|
}
|
|
|
|
/* Set the format marker boolean to false. */
|
|
insideMarker = FALSE;
|
|
|
|
/* Start parsing the character string. */
|
|
for (from = start; from < len; from++)
|
|
{
|
|
/* Are we inside a format marker? */
|
|
if (!insideMarker)
|
|
{
|
|
if (string[from] == L_MARKER
|
|
&& (string[from + 1] == '/'
|
|
|| string[from + 1] == '!'
|
|
|| string[from + 1] == '#'))
|
|
{
|
|
insideMarker = TRUE;
|
|
}
|
|
else if (string[from] == '\\' && string[from + 1] == L_MARKER)
|
|
{
|
|
from++;
|
|
if (result != 0)
|
|
result[used] = CharOf (string[from]) | attrib;
|
|
used++;
|
|
from++;
|
|
}
|
|
else if (string[from] == '\t')
|
|
{
|
|
do
|
|
{
|
|
if (result != 0)
|
|
result[used] = ' ';
|
|
used++;
|
|
}
|
|
while (used & 7);
|
|
}
|
|
else
|
|
{
|
|
if (result != 0)
|
|
result[used] = CharOf (string[from]) | attrib;
|
|
used++;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
switch (string[from])
|
|
{
|
|
case R_MARKER:
|
|
insideMarker = 0;
|
|
break;
|
|
case '#':
|
|
{
|
|
lastChar = 0;
|
|
switch (string[from + 2])
|
|
{
|
|
case 'L':
|
|
switch (string[from + 1])
|
|
{
|
|
case 'L':
|
|
lastChar = ACS_LLCORNER;
|
|
break;
|
|
case 'U':
|
|
lastChar = ACS_ULCORNER;
|
|
break;
|
|
case 'H':
|
|
lastChar = ACS_HLINE;
|
|
break;
|
|
case 'V':
|
|
lastChar = ACS_VLINE;
|
|
break;
|
|
case 'P':
|
|
lastChar = ACS_PLUS;
|
|
break;
|
|
}
|
|
break;
|
|
case 'R':
|
|
switch (string[from + 1])
|
|
{
|
|
case 'L':
|
|
lastChar = ACS_LRCORNER;
|
|
break;
|
|
case 'U':
|
|
lastChar = ACS_URCORNER;
|
|
break;
|
|
}
|
|
break;
|
|
case 'T':
|
|
switch (string[from + 1])
|
|
{
|
|
case 'T':
|
|
lastChar = ACS_TTEE;
|
|
break;
|
|
case 'R':
|
|
lastChar = ACS_RTEE;
|
|
break;
|
|
case 'L':
|
|
lastChar = ACS_LTEE;
|
|
break;
|
|
case 'B':
|
|
lastChar = ACS_BTEE;
|
|
break;
|
|
}
|
|
break;
|
|
case 'A':
|
|
switch (string[from + 1])
|
|
{
|
|
case 'L':
|
|
lastChar = ACS_LARROW;
|
|
break;
|
|
case 'R':
|
|
lastChar = ACS_RARROW;
|
|
break;
|
|
case 'U':
|
|
lastChar = ACS_UARROW;
|
|
break;
|
|
case 'D':
|
|
lastChar = ACS_DARROW;
|
|
break;
|
|
}
|
|
break;
|
|
default:
|
|
if (string[from + 1] == 'D' &&
|
|
string[from + 2] == 'I')
|
|
lastChar = ACS_DIAMOND;
|
|
else if (string[from + 1] == 'C' &&
|
|
string[from + 2] == 'B')
|
|
lastChar = ACS_CKBOARD;
|
|
else if (string[from + 1] == 'D' &&
|
|
string[from + 2] == 'G')
|
|
lastChar = ACS_DEGREE;
|
|
else if (string[from + 1] == 'P' &&
|
|
string[from + 2] == 'M')
|
|
lastChar = ACS_PLMINUS;
|
|
else if (string[from + 1] == 'B' &&
|
|
string[from + 2] == 'U')
|
|
lastChar = ACS_BULLET;
|
|
else if (string[from + 1] == 'S' &&
|
|
string[from + 2] == '1')
|
|
lastChar = ACS_S1;
|
|
else if (string[from + 1] == 'S' &&
|
|
string[from + 2] == '9')
|
|
lastChar = ACS_S9;
|
|
}
|
|
|
|
if (lastChar != 0)
|
|
{
|
|
adjust = 1;
|
|
from += 2;
|
|
|
|
if (string[from + 1] == '(')
|
|
/* Check for a possible numeric modifier. */
|
|
{
|
|
from++;
|
|
adjust = 0;
|
|
|
|
while (string[++from] != ')' && string[from] != 0)
|
|
{
|
|
if (isdigit (CharOf (string[from])))
|
|
{
|
|
adjust = (adjust * 10) + DigitOf (string[from]);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
for (x = 0; x < adjust; x++)
|
|
{
|
|
if (result != 0)
|
|
result[used] = lastChar | attrib;
|
|
used++;
|
|
}
|
|
break;
|
|
}
|
|
case '/':
|
|
from = encodeAttribute (string, from, &mask);
|
|
attrib = attrib | mask;
|
|
break;
|
|
case '!':
|
|
from = encodeAttribute (string, from, &mask);
|
|
attrib = attrib & ~mask;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
if (result != 0)
|
|
{
|
|
result[used] = 0;
|
|
result[used + 1] = 0;
|
|
}
|
|
|
|
/*
|
|
* If there are no characters, put the attribute into the
|
|
* the first character of the array.
|
|
*/
|
|
if (used == 0
|
|
&& result != 0)
|
|
{
|
|
result[0] = attrib;
|
|
}
|
|
}
|
|
*to = used;
|
|
}
|
|
else
|
|
{
|
|
/*
|
|
* Try always to return something; otherwise lists of chtype strings
|
|
* would get a spurious null pointer whenever there is a blank line,
|
|
* and CDKfreeChtypes() would fail to free the whole list.
|
|
*/
|
|
result = typeCallocN (chtype, 1);
|
|
}
|
|
return result;
|
|
}
|
|
|
|
/*
|
|
* This determines the length of a chtype string
|
|
*/
|
|
int chlen (const chtype *string)
|
|
{
|
|
int result = 0;
|
|
|
|
if (string != 0)
|
|
{
|
|
while (string[result] != 0)
|
|
result++;
|
|
}
|
|
|
|
return (result);
|
|
}
|
|
|
|
/*
|
|
* Compare a regular string to a chtype string
|
|
*/
|
|
int cmpStrChstr (const char *str, const chtype *chstr)
|
|
{
|
|
int r = 0;
|
|
|
|
if (!str && !chstr)
|
|
return 0;
|
|
if (!str)
|
|
return 1;
|
|
if (!chstr)
|
|
return -1;
|
|
|
|
while (!r && *str && *chstr)
|
|
{
|
|
r = *str - CharOf (*chstr);
|
|
++str;
|
|
++chstr;
|
|
}
|
|
|
|
if (r)
|
|
return r;
|
|
else if (!*str)
|
|
return -1;
|
|
else if (!*chstr)
|
|
return 1;
|
|
return 0;
|
|
}
|
|
|
|
void chstrncpy (char *dest, const chtype *src, int maxcount)
|
|
{
|
|
int i = 0;
|
|
|
|
while (i < maxcount && *src)
|
|
*dest++ = (char)(*src++);
|
|
|
|
*dest = '\0';
|
|
}
|
|
|
|
/*
|
|
* This returns a pointer to char * of a chtype *
|
|
* Formatting codes are omitted.
|
|
*/
|
|
char *chtype2Char (const chtype *string)
|
|
{
|
|
char *newstring = 0;
|
|
|
|
if (string != 0)
|
|
{
|
|
int len = chlen (string);
|
|
|
|
if ((newstring = typeMallocN (char, len + 1)) != 0)
|
|
{
|
|
int x;
|
|
|
|
for (x = 0; x < len; x++)
|
|
{
|
|
newstring[x] = (char)CharOf (string[x]);
|
|
}
|
|
newstring[len] = '\0';
|
|
}
|
|
}
|
|
return (newstring);
|
|
}
|
|
|
|
/*
|
|
* This returns a pointer to char * of a chtype *
|
|
* Formatting codes are embedded.
|
|
*/
|
|
char *chtype2String (const chtype *string)
|
|
{
|
|
char *newstring = 0;
|
|
|
|
if (string != 0)
|
|
{
|
|
int pass;
|
|
int len = chlen (string);
|
|
|
|
for (pass = 0; pass < 2; ++pass)
|
|
{
|
|
int x;
|
|
unsigned need = 0;
|
|
|
|
for (x = 0; x < len; ++x)
|
|
{
|
|
need = decodeAttribute (newstring, need,
|
|
(x > 0) ? string[x - 1] : 0,
|
|
string[x]);
|
|
if (newstring != 0)
|
|
newstring[need] = (char)(string[x]);
|
|
++need;
|
|
}
|
|
if (pass)
|
|
newstring[need] = 0;
|
|
++need;
|
|
if (!pass)
|
|
{
|
|
if ((newstring = typeMallocN (char, need)) == 0)
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
return (newstring);
|
|
}
|
|
|
|
static int comparSort (const void *a, const void *b)
|
|
{
|
|
return strcmp (*(const char *const *)a, (*(const char *const *)b));
|
|
}
|
|
|
|
void sortList (CDK_CSTRING *list, int length)
|
|
{
|
|
if (length > 1)
|
|
qsort (list, (unsigned)length, sizeof (list[0]), comparSort);
|
|
}
|
|
|
|
/*
|
|
* This strips white space from the given string.
|
|
*/
|
|
void stripWhiteSpace (EStripType stripType, char *string)
|
|
{
|
|
/* Declare local variables. */
|
|
size_t stringLength = 0;
|
|
|
|
/* Make sure the string is not null. */
|
|
if (string != 0
|
|
&& (stringLength = strlen (string)) != 0)
|
|
{
|
|
/* Strip leading whitespace */
|
|
if (stripType == vFRONT || stripType == vBOTH)
|
|
{
|
|
unsigned alphaChar = 0;
|
|
unsigned x;
|
|
|
|
/* Find the first non-whitespace character. */
|
|
while (string[alphaChar] == ' ' || string[alphaChar] == '\t')
|
|
{
|
|
alphaChar++;
|
|
}
|
|
|
|
for (x = alphaChar; x <= stringLength; ++x)
|
|
string[x - alphaChar] = string[x];
|
|
}
|
|
|
|
/* Strip trailing whitespace */
|
|
if (stripType == vBACK || stripType == vBOTH)
|
|
{
|
|
stringLength = strlen (string);
|
|
while (stringLength-- != 0 &&
|
|
(string[stringLength] == ' ' ||
|
|
string[stringLength] == '\t'))
|
|
{
|
|
string[stringLength] = '\0';
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
static unsigned countChar (const char *string, int separator)
|
|
{
|
|
unsigned result = 0;
|
|
int ch;
|
|
|
|
while ((ch = *string++) != 0)
|
|
{
|
|
if (ch == separator)
|
|
result++;
|
|
}
|
|
return result;
|
|
}
|
|
|
|
/*
|
|
* Split a string into a list of strings.
|
|
*/
|
|
char **CDKsplitString (const char *string, int separator)
|
|
{
|
|
char **result = 0;
|
|
char *temp;
|
|
|
|
if (string != 0 && *string != 0)
|
|
{
|
|
unsigned need = countChar (string, separator) + 2;
|
|
if ((result = typeMallocN (char *, need)) != 0)
|
|
{
|
|
unsigned item = 0;
|
|
const char *first = string;
|
|
for (;;)
|
|
{
|
|
while (*string != 0 && *string != separator)
|
|
string++;
|
|
|
|
need = (unsigned)(string - first);
|
|
if ((temp = typeMallocN (char, need + 1)) == 0)
|
|
break;
|
|
|
|
memcpy (temp, first, need);
|
|
temp[need] = 0;
|
|
result[item++] = temp;
|
|
|
|
if (*string++ == 0)
|
|
break;
|
|
first = string;
|
|
}
|
|
result[item] = 0;
|
|
}
|
|
}
|
|
return result;
|
|
}
|
|
|
|
/*
|
|
* Add a new string to a list. Keep a null pointer on the end so we can use
|
|
* CDKfreeStrings() to deallocate the whole list.
|
|
*/
|
|
unsigned CDKallocStrings (char ***list, char *item, unsigned length, unsigned used)
|
|
{
|
|
unsigned need = 1;
|
|
|
|
while (need < length + 2)
|
|
need *= 2;
|
|
if (need > used)
|
|
{
|
|
used = need;
|
|
if (*list == 0)
|
|
{
|
|
*list = typeMallocN (char *, used);
|
|
}
|
|
else
|
|
{
|
|
*list = typeReallocN (char *, *list, used);
|
|
}
|
|
}
|
|
(*list)[length++] = copyChar (item);
|
|
(*list)[length] = 0;
|
|
return used;
|
|
}
|
|
|
|
/*
|
|
* Count the number of items in a list of strings.
|
|
*/
|
|
unsigned CDKcountStrings (CDK_CSTRING2 list)
|
|
{
|
|
unsigned result = 0;
|
|
if (list != 0)
|
|
{
|
|
while (*list++ != 0)
|
|
result++;
|
|
}
|
|
return result;
|
|
}
|
|
|
|
/*
|
|
* Free a list of strings, terminated by a null pointer.
|
|
*/
|
|
void CDKfreeStrings (char **list)
|
|
{
|
|
if (list != 0)
|
|
{
|
|
void *base = (void *)list;
|
|
while (*list != 0)
|
|
free (*list++);
|
|
free (base);
|
|
}
|
|
}
|
|
|
|
/*
|
|
* Free a list of chtype-strings, terminated by a null pointer.
|
|
*/
|
|
void CDKfreeChtypes (chtype **list)
|
|
{
|
|
if (list != 0)
|
|
{
|
|
void *base = (void *)list;
|
|
while (*list != 0)
|
|
{
|
|
freeChtype (*list++);
|
|
}
|
|
free (base);
|
|
}
|
|
}
|
|
|
|
int mode2Filetype (mode_t mode)
|
|
{
|
|
/* *INDENT-OFF* */
|
|
static const struct {
|
|
mode_t mode;
|
|
char code;
|
|
} table[] = {
|
|
#ifdef S_IFBLK
|
|
{ S_IFBLK, 'b' }, /* Block device */
|
|
#endif
|
|
{ S_IFCHR, 'c' }, /* Character device */
|
|
{ S_IFDIR, 'd' }, /* Directory */
|
|
{ S_IFREG, '-' }, /* Regular file */
|
|
#ifdef S_IFLNK
|
|
{ S_IFLNK, 'l' }, /* Socket */
|
|
#endif
|
|
#ifdef S_IFSOCK
|
|
{ S_IFSOCK, '@' }, /* Socket */
|
|
#endif
|
|
{ S_IFIFO, '&' }, /* Pipe */
|
|
};
|
|
/* *INDENT-ON* */
|
|
|
|
int filetype = '?';
|
|
unsigned n;
|
|
|
|
for (n = 0; n < sizeof (table) / sizeof (table[0]); n++)
|
|
{
|
|
if ((mode & S_IFMT) == table[n].mode)
|
|
{
|
|
filetype = table[n].code;
|
|
break;
|
|
}
|
|
}
|
|
|
|
return filetype;
|
|
|
|
}
|
|
|
|
/*
|
|
* This function takes a mode_t type and creates a string represntation
|
|
* of the permission mode.
|
|
*/
|
|
int mode2Char (char *string, mode_t mode)
|
|
{
|
|
/* *INDENT-OFF* */
|
|
static struct {
|
|
mode_t mask;
|
|
unsigned col;
|
|
char flag;
|
|
} table[] = {
|
|
{ S_IRUSR, 1, 'r' },
|
|
{ S_IWUSR, 2, 'w' },
|
|
{ S_IXUSR, 3, 'x' },
|
|
#if defined (S_IRGRP) && defined (S_IWGRP) && defined (S_IXGRP)
|
|
{ S_IRGRP, 4, 'r' },
|
|
{ S_IWGRP, 5, 'w' },
|
|
{ S_IXGRP, 6, 'x' },
|
|
#endif
|
|
#if defined (S_IROTH) && defined (S_IWOTH) && defined (S_IXOTH)
|
|
{ S_IROTH, 7, 'r' },
|
|
{ S_IWOTH, 8, 'w' },
|
|
{ S_IXOTH, 9, 'x' },
|
|
#endif
|
|
#ifdef S_ISUID
|
|
{ S_ISUID, 3, 's' },
|
|
#endif
|
|
#ifdef S_ISGID
|
|
{ S_ISGID, 6, 's' },
|
|
#endif
|
|
#ifdef S_ISVTX
|
|
{ S_ISVTX, 9, 't' },
|
|
#endif
|
|
};
|
|
/* *INDENT-ON* */
|
|
|
|
/* Declare local variables. */
|
|
int permissions = 0;
|
|
int filetype = mode2Filetype (mode);
|
|
unsigned n;
|
|
|
|
/* Clean the string. */
|
|
cleanChar (string, 11, '-');
|
|
string[11] = '\0';
|
|
|
|
if (filetype == '?')
|
|
return -1;
|
|
|
|
for (n = 0; n < sizeof (table) / sizeof (table[0]); n++)
|
|
{
|
|
if ((mode & table[n].mask) != 0)
|
|
{
|
|
string[table[n].col] = table[n].flag;
|
|
permissions |= (int)table[n].mask;
|
|
}
|
|
}
|
|
|
|
/* Check for unusual permissions. */
|
|
#ifdef S_ISUID
|
|
if (((mode & S_IXUSR) == 0) &&
|
|
((mode & S_IXGRP) == 0) &&
|
|
((mode & S_IXOTH) == 0) &&
|
|
(mode & S_ISUID) != 0)
|
|
{
|
|
string[3] = 'S';
|
|
}
|
|
#endif
|
|
|
|
return permissions;
|
|
}
|
|
|
|
/*
|
|
* This returns the length of the integer.
|
|
*/
|
|
int intlen (int value)
|
|
{
|
|
if (value < 0)
|
|
return 1 + intlen (-value);
|
|
else if (value >= 10)
|
|
return 1 + intlen (value / 10);
|
|
return 1;
|
|
}
|
|
|
|
/*
|
|
* This opens the current directory and reads the contents.
|
|
*/
|
|
int CDKgetDirectoryContents (const char *directory, char ***list)
|
|
{
|
|
/* Declare local variables. */
|
|
struct dirent *dirStruct;
|
|
int counter = 0;
|
|
DIR *dp;
|
|
unsigned used = 0;
|
|
|
|
/* Open the directory. */
|
|
dp = opendir (directory);
|
|
|
|
/* Could we open the directory? */
|
|
if (dp == 0)
|
|
{
|
|
return -1;
|
|
}
|
|
|
|
/* Read the directory. */
|
|
while ((dirStruct = readdir (dp)) != 0)
|
|
{
|
|
if (strcmp (dirStruct->d_name, "."))
|
|
used = CDKallocStrings (list, dirStruct->d_name,
|
|
(unsigned)counter++, used);
|
|
}
|
|
|
|
/* Close the directory. */
|
|
closedir (dp);
|
|
|
|
/* Sort the info. */
|
|
sortList ((CDK_CSTRING *)*list, counter);
|
|
|
|
/* Return the number of files in the directory. */
|
|
return counter;
|
|
}
|
|
|
|
/*
|
|
* This looks for a subset of a word in the given list.
|
|
*/
|
|
int searchList (CDK_CSTRING2 list, int listSize, const char *pattern)
|
|
{
|
|
int Index = -1;
|
|
|
|
/* Make sure the pattern isn't null. */
|
|
if (pattern != 0)
|
|
{
|
|
size_t len = strlen (pattern);
|
|
int x;
|
|
|
|
/* Cycle through the list looking for the word. */
|
|
for (x = 0; x < listSize; x++)
|
|
{
|
|
/* Do a string compare. */
|
|
int ret = strncmp (list[x], pattern, len);
|
|
|
|
/*
|
|
* If 'ret' is less than 0, then the current word is alphabetically
|
|
* less than the provided word. At this point we will set the index
|
|
* to the current position. If 'ret' is greater than 0, then the
|
|
* current word is alphabetically greater than the given word. We
|
|
* should return with index, which might contain the last best match.
|
|
* If they are equal, then we've found it.
|
|
*/
|
|
if (ret < 0)
|
|
{
|
|
Index = ret;
|
|
}
|
|
else
|
|
{
|
|
if (ret == 0)
|
|
Index = x;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
return Index;
|
|
}
|
|
|
|
/*
|
|
* This function checks to see if a link has been requested.
|
|
*/
|
|
int checkForLink (const char *line, char *filename)
|
|
{
|
|
int len = 0;
|
|
int fPos = 0;
|
|
|
|
/* Make sure the line isn't null. */
|
|
if (line == 0)
|
|
{
|
|
return -1;
|
|
}
|
|
len = (int)strlen (line);
|
|
|
|
/* Strip out the filename. */
|
|
if (line[0] == L_MARKER && line[1] == 'F' && line[2] == '=')
|
|
{
|
|
int x = 3;
|
|
|
|
/* Strip out the filename. */
|
|
while (x < len)
|
|
{
|
|
if (line[x] == R_MARKER)
|
|
{
|
|
break;
|
|
}
|
|
if (fPos < CDK_PATHMAX)
|
|
filename[fPos++] = line[x];
|
|
++x;
|
|
}
|
|
}
|
|
filename[fPos] = '\0';
|
|
return (fPos != 0);
|
|
}
|
|
|
|
/*
|
|
* Returns the filename portion of the given pathname, i.e., after the last
|
|
* slash.
|
|
*/
|
|
char *baseName (char *pathname)
|
|
{
|
|
char *base = 0;
|
|
|
|
if (pathname != 0
|
|
&& *pathname != '\0'
|
|
&& (base = copyChar (pathname)) != 0)
|
|
{
|
|
size_t pathLen;
|
|
|
|
if ((pathLen = strlen (pathname)) != 0)
|
|
{
|
|
size_t x;
|
|
|
|
for (x = pathLen - 1; x != 0; --x)
|
|
{
|
|
/* Find the last '/' in the pathname. */
|
|
if (pathname[x] == '/')
|
|
{
|
|
strcpy (base, pathname + x + 1);
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
return base;
|
|
}
|
|
|
|
/*
|
|
* Returns the directory for the given pathname, i.e., the part before the
|
|
* last slash.
|
|
*/
|
|
char *dirName (char *pathname)
|
|
{
|
|
char *dir = 0;
|
|
size_t pathLen;
|
|
|
|
/* Check if the string is null. */
|
|
if (pathname != 0
|
|
&& (dir = copyChar (pathname)) != 0
|
|
&& (pathLen = strlen (pathname)) != 0)
|
|
{
|
|
size_t x = pathLen;
|
|
while ((dir[x] != '/') && (x > 0))
|
|
{
|
|
dir[x--] = '\0';
|
|
}
|
|
}
|
|
|
|
return dir;
|
|
}
|
|
|
|
/*
|
|
* If the dimension is a negative value, the dimension will be the full
|
|
* height/width of the parent window - the value of the dimension. Otherwise,
|
|
* the dimension will be the given value.
|
|
*/
|
|
int setWidgetDimension (int parentDim, int proposedDim, int adjustment)
|
|
{
|
|
int dimension = 0;
|
|
|
|
/* If the user passed in FULL, return the parent's size. */
|
|
if ((proposedDim == FULL) || (proposedDim == 0))
|
|
{
|
|
dimension = parentDim;
|
|
}
|
|
else
|
|
{
|
|
/* If they gave a positive value, return it. */
|
|
if (proposedDim >= 0)
|
|
{
|
|
if (proposedDim >= parentDim)
|
|
dimension = parentDim;
|
|
else
|
|
dimension = (proposedDim + adjustment);
|
|
}
|
|
else
|
|
{
|
|
/*
|
|
* If they gave a negative value, then return the
|
|
* dimension of the parent plus the value given.
|
|
*/
|
|
dimension = parentDim + proposedDim;
|
|
|
|
/* Just to make sure. */
|
|
if (dimension < 0)
|
|
dimension = parentDim;
|
|
}
|
|
}
|
|
return dimension;
|
|
}
|
|
|
|
/*
|
|
* This safely erases a given window.
|
|
*/
|
|
void eraseCursesWindow (WINDOW *window)
|
|
{
|
|
if (window != 0)
|
|
{
|
|
werase (window);
|
|
wrefresh (window);
|
|
}
|
|
}
|
|
|
|
/*
|
|
* This safely deletes a given window.
|
|
*/
|
|
void deleteCursesWindow (WINDOW *window)
|
|
{
|
|
if (window != 0)
|
|
{
|
|
eraseCursesWindow (window);
|
|
delwin (window);
|
|
}
|
|
}
|
|
|
|
/*
|
|
* This moves a given window (if we're able to set the window's beginning).
|
|
* We do not use mvwin(), because it does (usually) not move subwindows.
|
|
*/
|
|
void moveCursesWindow (WINDOW *window, int xdiff, int ydiff)
|
|
{
|
|
if (window != 0)
|
|
{
|
|
int xpos, ypos;
|
|
|
|
getbegyx (window, ypos, xpos);
|
|
(void)setbegyx (window, (short)ypos, (short)xpos);
|
|
xpos += xdiff;
|
|
ypos += ydiff;
|
|
werase (window);
|
|
(void)setbegyx (window, (short)ypos, (short)xpos);
|
|
}
|
|
}
|
|
|
|
/*
|
|
* Return an integer like 'floor()', which returns a double.
|
|
*/
|
|
int floorCDK (double value)
|
|
{
|
|
int result = (int)value;
|
|
if (result > value) /* e.g., value < 0.0 and value is not an integer */
|
|
result--;
|
|
return result;
|
|
}
|
|
|
|
/*
|
|
* Return an integer like 'ceil()', which returns a double.
|
|
*/
|
|
int ceilCDK (double value)
|
|
{
|
|
return -floorCDK (-value);
|
|
}
|
|
|
|
/*
|
|
* Compatibility for different versions of curses.
|
|
*/
|
|
#if !(defined(HAVE_GETBEGX) && defined(HAVE_GETBEGY))
|
|
int getbegx (WINDOW *win)
|
|
{
|
|
int y, x;
|
|
getbegyx (win, y, x);
|
|
return x;
|
|
}
|
|
int getbegy (WINDOW *win)
|
|
{
|
|
int y, x;
|
|
getbegyx (win, y, x);
|
|
return y;
|
|
}
|
|
#endif
|
|
|
|
#if !(defined(HAVE_GETMAXX) && defined(HAVE_GETMAXY))
|
|
int getmaxx (WINDOW *win)
|
|
{
|
|
int y, x;
|
|
getmaxyx (win, y, x);
|
|
return x;
|
|
}
|
|
int getmaxy (WINDOW *win)
|
|
{
|
|
int y, x;
|
|
getmaxyx (win, y, x);
|
|
return y;
|
|
}
|
|
#endif
|