//
// aegis - project change supervisor
// Copyright (C) 1997, 1998, 2002-2006, 2008, 2012, 2014 Peter Miller
//
// 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 3 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.
//
// You should have received a copy of the GNU General Public License
// along with this program. If not, see .
//
#include
#include
#include
#include
#include
#include
#include
#if 0
static nstring
ary_look(const nstring_list &x)
{
nstring_list temp;
size_t tot = 0;
for (size_t j = 0; j < x.size(); ++j)
{
temp.push_back(x[j].quote_c());
tot += x[j].size();
}
return "{ " + temp.unsplit(", ") + "}" + nstring::format(" // %zu", tot);
}
#endif
#ifdef DEBUG
#endif
#ifdef DEBUG
static size_t
inner_size(const nstring_list &x)
{
size_t result = 0;;
for (size_t j = 0; j < x.size(); ++j)
{
result += x[j].size();
}
return result;
}
#endif
nstring
abbreviate(const nstring &s, size_t max_len, bool keep_last_dot)
{
//
// trivial sanity check
//
assert(max_len > 0);
if (s.size() <= max_len)
return s;
if (!max_len)
return nstring();
//
// Break it all up into punctuation and words.
// The punctuation *preceeds* the word.
//
nstring_list word;
nstring_list punct;
size_t punct_total = 0;
size_t word_total = 0;
size_t total = 0;
const char *cp = s.c_str();
while (*cp)
{
const char *start = cp;
while (*cp && !isalnum((unsigned char)*cp))
++cp;
nstring s1(start, cp - start);
punct.push_back(s1);
punct_total += s1.size();
total += s1.size();
start = cp;
if (*cp && isdigit((unsigned char)*cp))
{
// treat numbers a whole words
while (*cp && isdigit((unsigned char)*cp))
++cp;
}
else
{
// if thet UsedCamelCase, treat each camel as a whole word
bool was_upper = true;
while (*cp && isalpha((unsigned char)*cp))
{
bool is_upper = isupper((unsigned char)*cp);
if (!was_upper && is_upper)
break;
was_upper = is_upper;
++cp;
}
}
nstring s2(start, cp - start);
word.push_back(s2);
word_total += s2.size();
total += s2.size();
}
assert(punct.size()); // can't be empty string
assert(punct.size() == word.size()); // FIXME: test this
assert(punct_total + word_total == total);
assert(punct_total == inner_size(punct));
assert(word_total == inner_size(word));
//
// kill leading punctuation
//
if (!punct.front().empty())
{
total -= punct.front().size();
punct_total -= punct.front().size();
punct[0] = nstring();
if (total <= max_len)
goto reassemble;
}
//
// kill trailing punctuation
//
if (!punct.back().empty() && word.back().empty())
{
total -= punct.back().size();
punct_total -= punct.back().size();
punct.pop_back();
word.pop_back();
if (total <= max_len)
goto reassemble;
}
assert(punct.size());
assert(punct.size() == word.size());
assert(punct.size() == word.size());
assert(punct_total + word_total == total);
assert(punct_total == inner_size(punct));
assert(word_total == inner_size(word));
//
// shorten punctuation to one character each
// (j must be a signed type)
//
for (int j = punct.size() - 1; j >= 0; --j)
{
if (punct[j].size() < 2)
continue;
total -= punct[j].size() - 1;
punct_total -= punct[j].size() - 1;
nstring s1 = punct[j].substr(0, 1);
punct[j] = s1;
if (total <= max_len)
goto reassemble;
}
assert(punct.size() == word.size());
assert(punct_total + word_total == total);
assert(punct_total == inner_size(punct));
assert(word_total == inner_size(word));
//
// remove all punctuation if we are very squeezed for space
// (except last dot, for file extension).
//
if (punct_total * 5 > max_len)
{
nstring dot(".");
if (keep_last_dot && punct.back() == dot)
{
punct.clear();
total -= punct_total;
punct_total = 0;
while (punct.size() + 1 < word.size())
{
punct.push_back(nstring());
}
punct.push_back(dot);
punct_total = 1;
++total;
}
else
{
punct.clear();
total -= punct_total;
punct_total = 0;
while (punct.size() < word.size())
punct.push_back(nstring());
}
}
assert(punct.size() == word.size());
assert(punct_total + word_total == total);
assert(punct_total == inner_size(punct));
assert(word_total == inner_size(word));
//
// shorten all the words
//
{
size_t word_max = 1;
for (size_t k = 0; k < word.size(); ++k)
if (word[k].size() > word_max)
word_max = word[k].size();
for (--word_max; word_max >= 1; --word_max)
{
// j must be a signed type
for (int j = word.size() - 1; j >= 0; --j)
{
nstring w = word[j];
if (w.size() <= word_max)
continue;
word_total -= w.size();
total -= w.size();
nstring s1 = w.substr(0, word_max);
word[j] = s1;
word_total += s1.size();
total += s1.size();
if (total <= max_len)
goto reassemble;
}
}
}
assert(punct.size() == word.size());
assert(punct_total + word_total == total);
assert(punct_total == inner_size(punct));
assert(word_total == inner_size(word));
reassemble:
nstring_accumulator ac;
for (size_t k = 0; k < punct.size(); ++k)
{
ac.push_back(punct[k]);
ac.push_back(word[k]);
}
return ac.mkstr();
}
static nstring
nuke_unprintable(const nstring &s)
{
nstring_accumulator buffer;
const char *ip = s.c_str();
for (;;)
{
unsigned char c = *ip++;
if (!c)
break;
if (isspace(c) || !isprint(c))
c = '_';
buffer.push_back(c);
}
return buffer.mkstr();
}
nstring
abbreviate_dirname(const nstring &s, size_t max_len)
{
nstring s2 = nuke_unprintable(s);
nstring result = abbreviate(s2, max_len, false);
return result;
}
nstring
abbreviate_filename(const nstring &s, size_t max_len)
{
nstring s2 = nuke_unprintable(s);
nstring result = abbreviate(s2, max_len, true);
return result;
}
static bool
contains_moronic_ms_restrictions(const nstring &fn)
{
static const char *const moronic[] =
{
"aux",
"com1",
"com2",
"com3",
"com4",
"con",
"nul",
};
static nstring_list wl;
if (wl.size() == 0)
{
for (const char *const *cpp = moronic; cpp < ENDOF(moronic); ++cpp)
{
nstring fn2(*cpp);
wl.push_back(fn2);
}
}
nstring fn2 = fn.downcase();
return wl.member(fn2);
}
nstring
abbreviate_8dos3(const nstring &s)
{
nstring s1 = nuke_unprintable(s);
const char *cp = strrchr(s1.c_str(), '.');
if (!cp)
return abbreviate(s1, 8, 0);
// the part before the dot
size_t nbytes2 = cp - s1.c_str();
nstring s2 = s1.substr(0, nbytes2);
nstring s2a = abbreviate(s2, 8, 0);
if
(
s2a.empty()
||
!isalpha((unsigned char)s2a[0])
||
contains_moronic_ms_restrictions(s2a)
)
{
nstring s4 = "a" + s2;
s2a = abbreviate(s4, 8, 0);
}
// the part after the dot
++cp;
size_t start3 = cp - s1.c_str();
size_t nbytes3 = s1.size() - start3;
nstring s3 = s1.substr(start3, nbytes3);
nstring s3a = abbreviate(s3, 3, 0);
// reassemble
nstring result = (s3a.empty() ? s2a : s2a + "." + s3a);
return result;
}
// vim: set ts=8 sw=4 et :