// // 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 :