tuxpaint-pencil-sharpener/src/dirwalk.c
Mark K. Kim 4cabed88a6 Improved support for detecting missing dependencies:
- Added libpaper-dev package checking.

- Previously, SDL_Pango's bug forced Tux Paint to include a customized
  version of SDL_Pango.h in its distribution, so we couldn't tell if
  SDL_Pango was installed in the system by including SDL_Pango.h.  We made
  a new workaround for SDL_Pango's bug that does not require Tux Paint to
  include SDL_Pango.h, so now we can check for SDL_Pango installation.

- Makefile now includes a check for gettext installation and will not
  fail when attempting to generate .mo files or install .mo files.  Instead,
  Makefile will now display a warning and skip the .mo file generation and
  installation steps.

Many thanks to Arunodai Vudem for the patches.
2008-05-25 02:30:45 +00:00

348 lines
10 KiB
C

/*
dirwalk.c
*/
#include <stdio.h>
#include <stdlib.h>
#ifndef __USE_GNU
#define __USE_GNU /* for strcasestr() */
#endif
#include <string.h>
#include <locale.h>
#include <libintl.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <unistd.h>
#include <dirent.h>
#include "SDL_ttf.h"
#include "dirwalk.h"
#include "progressbar.h"
/*
The following section renames global variables defined in SDL_Pango.h to avoid errors during linking.
It is okay to rename these variables because they are constants.
SDL_Pang.h is included by fonts.h.
*/
#define _MATRIX_WHITE_BACK _MATRIX_WHITE_BACK1
#define MATRIX_WHITE_BACK MATRIX_WHITE_BACK1
#define _MATRIX_BLACK_BACK _MATRIX_BLACK_BACK1
#define MATRIX_BLACK_BACK MATRIX_BLACK_BACK1
#define _MATRIX_TRANSPARENT_BACK_BLACK_LETTER _MATRIX_TRANSPARENT_BACK_BLACK_LETTER1
#define MATRIX_TRANSPARENT_BACK_BLACK_LETTER MATRIX_TRANSPARENT_BACK_BLACK_LETTER1
#define _MATRIX_TRANSPARENT_BACK_WHITE_LETTER _MATRIX_TRANSPARENT_BACK_WHITE_LETTER1
#define MATRIX_TRANSPARENT_BACK_WHITE_LETTER MATRIX_TRANSPARENT_BACK_WHITE_LETTER1
#define _MATRIX_TRANSPARENT_BACK_TRANSPARENT_LETTER _MATRIX_TRANSPARENT_BACK_TRANSPARENT_LETTER1
#define MATRIX_TRANSPARENT_BACK_TRANSPARENT_LETTER MATRIX_TRANSPARENT_BACK_TRANSPARENT_LETTER1
/*
The renaming ends here.
*/
#include "fonts.h"
#include "debug.h"
///////////////// directory walking callers and callbacks //////////////////
void loadfont_callback(SDL_Surface * screen, const char *restrict const dir,
unsigned dirlen, tp_ftw_str * files, unsigned i)
{
dirlen = dirlen;
while (i--)
{
int loadable = 0;
const char *restrict const cp = strchr(files[i].str, '.');
show_progress_bar(screen);
if (cp)
{
// need gcc 3.4 for the restrict in this location
const char * /*restrict */ const suffixes[] =
{ "ttc", "dfont", "pfa", "pfb", "otf", "ttf", };
int j = sizeof suffixes / sizeof suffixes[0];
while (j--)
{
// only check part, because of potential .gz or .bz2 suffix
if (!strncasecmp(cp + 1, suffixes[j], strlen(suffixes[j])))
{
loadable = 1;
break;
}
}
}
if (!loadable)
{
if (strcasestr(files[i].str, "/rsrc"))
loadable = 1;
}
// Loadable: TrueType (.ttf), OpenType (.otf), Type1 (.pfa and .pfb),
// and various useless bitmap fonts. Compressed files (with .gz or .bz2)
// should also work. A *.dfont is pretty much a Mac resource fork in a normal
// file, and may load with some library versions.
if (loadable)
{
char fname[512];
TuxPaint_Font *font;
snprintf(fname, sizeof fname, "%s/%s", dir, files[i].str);
//printf("Loading font: %s\n", fname);
font = TuxPaint_Font_OpenFont("", fname, text_sizes[text_size]);
if (font)
{
const char *restrict const family = TuxPaint_Font_FontFaceFamilyName(font);
const char *restrict const style = TuxPaint_Font_FontFaceStyleName(font);
#ifdef DEBUG
int numfaces = TTF_FontFaces(font->ttf_font);
if (numfaces != 1)
printf("Found %d faces in %s, %s, %s\n", numfaces, files[i].str,
family, style);
printf("success: tpf: 0x%x tpf->ttf_font: 0x%x\n",
(unsigned int) font, (unsigned int) font->ttf_font);
#endif
// First, the blacklist. We list font families that can crash Tux Paint
// via bugs in the SDL_ttf library. We also test fonts to be sure that
// they have both uppercase and lowercase letters. Note that we do not
// test for "Aa", because it is OK if uppercase and lowercase are the
// same (but not nice -- such fonts get a low score later).
//
// We test the alphabet twice, to help with translation. If the users
// will be unable to type ASCII letters, then both Line X and Line Y
// should be translated. Otherwise, only Line X should be translated
// and the ASCII-only fonts should be given bad scores in the scoring
// code below (the best scores going to fonts that support both).
// Line X
if (strcmp("Zapfino", family) && strcmp("Elvish Ring NFI", family) && ((charset_works(font, gettext("qx")) && charset_works(font, gettext("QX")))
// Line Y
|| (charset_works(font, gettext("qy")) && charset_works(font, gettext("QY")))
))
{
if (num_font_styles == num_font_styles_max)
{
num_font_styles_max = num_font_styles_max * 5 / 4 + 30;
user_font_styles =
realloc(user_font_styles,
num_font_styles_max * sizeof *user_font_styles);
}
user_font_styles[num_font_styles] =
malloc(sizeof *user_font_styles[num_font_styles]);
user_font_styles[num_font_styles]->directory = strdup(dir);
user_font_styles[num_font_styles]->filename = files[i].str; // steal it (mark NULL below)
user_font_styles[num_font_styles]->family = strdup(family);
user_font_styles[num_font_styles]->style = strdup(style);
// Now we score fonts to ensure that the best ones will be placed at
// the top of the list. The user will see them first. This sorting is
// especially important for users who have scroll buttons disabled.
// Translators should do whatever is needed to put crummy fonts last.
// distinct uppercase and lowercase (e.g., 'o' vs. 'O')
user_font_styles[num_font_styles]->score = charset_works(font, gettext("oO"));
// uncommon punctuation (e.g., '@', '#', '*', etc.)
user_font_styles[num_font_styles]->score += charset_works(font, gettext("`\%_@$~#{}<>^&*"));
// common punctuation (e.g., '?', '!', '.', ',', etc.)
user_font_styles[num_font_styles]->score += charset_works(font, gettext(",.?!"));
// digits (e.g., '0', '1' and '7')
user_font_styles[num_font_styles]->score += charset_works(font, gettext("017"));
// distinct circle-like characters (e.g., 'O' (capital oh) vs. '0' (zero))
user_font_styles[num_font_styles]->score += charset_works(font, gettext("O0"));
// distinct line-like characters (e.g., 'l' (lowercase elle) vs. '1' (one) vs. 'I' (capital aye))
user_font_styles[num_font_styles]->score += charset_works(font, gettext("1Il|"));
#ifdef OLPC_XO
user_font_styles[num_font_styles]->score += charset_works(font, "\xc3\x97\xc3\xb7"); // multiply and divide
#endif
num_font_styles++;
//printf("Accepted: %s, %s, %s, score(%d)\n", files[i].str, family, style, user_font_styles[num_font_styles]->score);
files[i].str = NULL; // so free() won't crash -- we stole the memory
}
else
{
#if 0
// THREADED_FONTS
printf("Font is too defective: %s, %s, %s\n", files[i].str, family,
style);
#endif
}
TuxPaint_Font_CloseFont(font);
}
else
{
#if 0
// THREADED_FONTS
printf("could not open %s\n", files[i].str);
#endif
}
}
free(files[i].str);
}
free(files);
}
// For qsort()
int compare_ftw_str(const void *v1, const void *v2)
{
const char *restrict const s1 = ((tp_ftw_str *) v1)->str;
const char *restrict const s2 = ((tp_ftw_str *) v2)->str;
return -strcmp(s1, s2);
}
void tp_ftw(SDL_Surface * screen, char *restrict const dir, unsigned dirlen,
int rsrc, void (*fn) (SDL_Surface * screen,
const char *restrict const dir,
unsigned dirlen, tp_ftw_str * files,
unsigned count))
{
DIR *d;
unsigned num_file_names = 0;
unsigned max_file_names = 0;
tp_ftw_str *file_names = NULL;
unsigned num_dir_names = 0;
unsigned max_dir_names = 0;
tp_ftw_str *dir_names = NULL;
int d_namlen;
int add_rsrc;
dir[dirlen++] = '/';
dir[dirlen] = '\0';
//printf("processing directory %s %d\n", dir, dirlen);
/* Open the directory: */
d = opendir(dir);
if (!d)
return;
for (;;)
{
struct dirent *f = readdir(d);
int filetype = TP_FTW_UNKNOWN;
if (!f)
break;
if (f->d_name[0] == '.')
continue;
// Linux and BSD can often provide file type info w/o the stat() call
#ifdef DT_UNKNOWN
switch (f->d_type)
{
default:
continue;
case DT_REG:
if (!rsrc) // if maybe opening resource files, need st_size
filetype = TP_FTW_NORMAL;
break;
case DT_DIR:
filetype = TP_FTW_DIRECTORY;
break;
case DT_UNKNOWN:
case DT_LNK:
;
}
#else
#warning Failed to see DT_UNKNOWN
#endif
#if defined(_DIRENT_HAVE_D_NAMLEN) || defined(__APPLE__) || defined(__OpenBSD__) || defined(__NetBSD__) || defined(__FreeBSD__)
d_namlen = f->d_namlen;
#else
d_namlen = strlen(f->d_name);
#endif
add_rsrc = 0;
if (filetype == TP_FTW_UNKNOWN)
{
struct stat sbuf;
memcpy(dir + dirlen, f->d_name, d_namlen + 1);
if (stat(dir, &sbuf))
continue; // oh well... try the next one
if (S_ISDIR(sbuf.st_mode))
filetype = TP_FTW_DIRECTORY;
else if (S_ISREG(sbuf.st_mode))
{
filetype = TP_FTW_NORMAL;
if (rsrc && !sbuf.st_size)
add_rsrc = 5; // 5 is length of "/rsrc"
}
else
continue; // was a device file or somesuch
}
if (filetype == TP_FTW_NORMAL)
{
char *cp;
if (num_file_names == max_file_names)
{
max_file_names = max_file_names * 5 / 4 + 30;
file_names = realloc(file_names, max_file_names * sizeof *file_names);
}
cp = malloc(d_namlen + add_rsrc + 1);
memcpy(cp, f->d_name, d_namlen);
if (add_rsrc)
memcpy(cp + d_namlen, "/rsrc", 6);
else
cp[d_namlen] = '\0';
file_names[num_file_names].str = cp;
file_names[num_file_names].len = d_namlen;
num_file_names++;
}
if (filetype == TP_FTW_DIRECTORY)
{
char *cp;
if (num_dir_names == max_dir_names)
{
max_dir_names = max_dir_names * 5 / 4 + 3;
dir_names = realloc(dir_names, max_dir_names * sizeof *dir_names);
}
cp = malloc(d_namlen + 1);
memcpy(cp, f->d_name, d_namlen + 1);
dir_names[num_dir_names].str = cp;
dir_names[num_dir_names].len = d_namlen;
num_dir_names++;
}
}
closedir(d);
show_progress_bar(screen);
dir[dirlen] = '\0'; // repair it (clobbered for stat() call above)
if (1 || file_names) // Now ALWAYS calling callback function, so stamp loader can notice top-level directories (even if there are only subdirs, and no files, inside) -bjk 2007.05.16
{
// let callee sort and keep the string
#if 0
qsort(file_names, num_file_names, sizeof *file_names, compare_ftw_str);
while (num_file_names--)
{
free(file_names[num_file_names].str);
}
free(file_names);
#else
fn(screen, dir, dirlen, file_names, num_file_names);
#endif
}
if (dir_names)
{
qsort(dir_names, num_dir_names, sizeof *dir_names, compare_ftw_str);
while (num_dir_names--)
{
memcpy(dir + dirlen, dir_names[num_dir_names].str,
dir_names[num_dir_names].len + 1);
tp_ftw(screen, dir, dirlen + dir_names[num_dir_names].len, rsrc, fn);
free(dir_names[num_dir_names].str);
}
free(dir_names);
}
}