#include <stdio.h>#include <stdlib.h>#include <stdint.h>#include <fcntl.h>#include <unistd.h>#include <sys/stat.h>#include <SDL_ttf.h>#include "parg.h"#include "common.h" #if !SDL_TTF_VERSION_ATLEAST(2,0,18)#error SDL2_ttf of version >= 2.0.18 required#else #ifndef MAX#define MAX(a,b) ((a)>(b)?(a):(b))#endif enum { Substitute = 0xfffd, Maxsubfwidth = 3000, /* rough */}; static uint8_t pjw[] = {#include "pjw.h"}; static char *opts = "d:hPs:x:H:S:";static SDL_Color white = {255, 255, 255, 255};static SDL_Color black = {0, 0, 0, 255};static char *opath, *prefix;static int ptsz, hint, style; static uint8_t *surf2k8(SDL_Surface *s){ uint8_t *b, *p; int sz, x, y; sz = s->w*s->h; b = p = malloc(sz); for(y = 0; y < s->h; y++){ for(x = 0; x < s->w; x++) *p++ = ((uint8_t*)s->pixels + y*s->pitch)[x*4]; } return b;} static intpjwit(int f, char *s, int ssz, int scale){ SDL_Surface *g, *line; int dummy, advance, r; SDL_RWops *ops; TTF_Font *pf; Fontchar fc; uint8_t *b; r = 0; g = NULL; b = NULL; line = NULL; dprintf(f, "0x%X\t0x%X\t%s.%X-%X\n", 0, 0, prefix, 0, 0); dprintf(f, "0x%X\t0x%X\t%s.%X-%X\n", Substitute, Substitute, prefix, Substitute, Substitute); if((ops = SDL_RWFromConstMem(pjw, sizeof(pjw))) == NULL){ fprintf(stderr, "pjw: %s\n", SDL_GetError()); return -1; } if((pf = TTF_OpenFontRW(ops, 1, ptsz*scale)) == NULL || TTF_SetFontSize(pf, ptsz*scale) != 0 || TTF_GlyphMetrics32(pf, Substitute, &dummy, &dummy, &dummy, &dummy, &advance) != 0 || (g = TTF_RenderGlyph32_Shaded(pf, Substitute, white, black)) == NULL || (line = SDL_CreateRGBSurfaceWithFormat(0, g->w, g->h, 32, SDL_PIXELFORMAT_XRGB8888)) == NULL) goto err; SDL_FillRect(line, NULL, SDL_MapRGBA(line->format, 0, 0, 0, 0)); SDL_BlitSurface(g, NULL, line, NULL); b = surf2k8(line); if(b == NULL) goto err; snprintf(s, ssz, "%s.%X-%X", opath, 0, 0); memset(&fc, 0, sizeof(fc)); fc.width = advance; fc.bottom = line->h; if(savesubfont(b, line->w, line->h, TTF_FontAscent(pf), 1, &fc, 1, s) != 0) goto err2; snprintf(s, ssz, "%s.%X-%X", opath, Substitute, Substitute); if(savesubfont(b, line->w, line->h, TTF_FontAscent(pf), 1, &fc, 1, s) != 0) goto err2; if(0){err: fprintf(stderr, "pjw: %s\n", TTF_GetError());err2: r = -1; } if(g != NULL) SDL_FreeSurface(g); if(line != NULL) SDL_FreeSurface(line); if(pf != NULL) TTF_CloseFont(pf); else SDL_RWclose(ops); free(b); return r;} intmain(int argc, char **argv){ int c, optend, nr, i, fdfont, ssz, res, dpi; int minx, maxx, advance, dummy, w, h, maxh, pjwface, hdr, scale; struct parg_state ps; Range *ranges, *rn; char *ofile, *s; TTF_Font *f; uint8_t *b; Uint32 r; if(TTF_Init() != 0){ fprintf(stderr, "ttf init failed: %s\n", TTF_GetError()); return 1; } dpi = -1; ptsz = 16; hint = TTF_HINTING_NORMAL; style = TTF_STYLE_NORMAL; pjwface = 1; scale = 1; parg_init(&ps); optend = parg_reorder(argc, argv, opts, NULL); while((c = parg_getopt(&ps, argc, argv, opts)) >= 0){ switch(c){ case 1: break; case 'd': dpi = atoi(ps.optarg); break; case 's': if((ptsz = atoi(ps.optarg)) < 2){ fprintf(stderr, "invalid point size: %s\n", ps.optarg); return 1; } break; case 'x': if((scale = atoi(ps.optarg)) < 1){ fprintf(stderr, "invalid scaling: %s\n", ps.optarg); return 1; } break; case 'H': if(strcmp(ps.optarg, "normal") == 0) hint = TTF_HINTING_NORMAL; else if(strcmp(ps.optarg, "light") == 0) hint = TTF_HINTING_LIGHT; else if(strcmp(ps.optarg, "mono") == 0) hint = TTF_HINTING_MONO; else if(strcmp(ps.optarg, "none") == 0) hint = TTF_HINTING_NONE; else if(strcmp(ps.optarg, "light_subpixel") == 0) hint = TTF_HINTING_LIGHT_SUBPIXEL; else{ fprintf(stderr, "invalid hinting: %s\n", ps.optarg); return 1; } break; case 'P': pjwface = 0; fprintf(stderr, "shame on you!\n"); break; case 'S': if(strcmp(ps.optarg, "normal") == 0) style = TTF_STYLE_NORMAL; else if(strcmp(ps.optarg, "bold") == 0) style |= TTF_STYLE_BOLD; else if(strcmp(ps.optarg, "italic") == 0) style |= TTF_STYLE_ITALIC; else if(strcmp(ps.optarg, "underline") == 0) style |= TTF_STYLE_UNDERLINE; else if(strcmp(ps.optarg, "strikethrough") == 0) style |= TTF_STYLE_STRIKETHROUGH; else{ fprintf(stderr, "invalid style: %s\n", ps.optarg); return 1; } break; case 'h':usage: fprintf(stderr, "usage: %s [-d DPI] [-s point_size] [-x scale] [-H hinting] [-S style [-S ...]] [-P] FILE [FILE ...] PATH/PREFIX\n", argv[0]); fprintf(stderr, " -H: normal (default), none, light, mono, light_subpixel\n"); fprintf(stderr, " -S: normal (default), bold, italic, underline, strikethrough\n"); fprintf(stderr, " -P: I don't want PJW face in my font (shame on you)\n"); return 1; case '?': fprintf(stderr, "unknown option -%c\n", ps.optopt); return 1; default: fprintf(stderr, "unhandled option -%c\n", c); return 1; } } if(argc-optend < 2) goto usage; opath = argv[argc-1]; i = strlen(opath); s = malloc(ssz = i+64); snprintf(s, ssz, "%s.font", opath); if((prefix = strrchr(s, '/')) == NULL) prefix = s; else{ *prefix++ = 0; mkdir(s, 0755); prefix[-1] = '/'; } res = 0; if((fdfont = open(s, O_CREAT|O_TRUNC|O_WRONLY|O_CLOEXEC, 0644)) < 0){ prefix = NULL; perror(s); goto err; } s[i] = 0; prefix = strdup(prefix); maxh = 0; for(hdr = 0; optend < argc-1; optend++){ ofile = argv[optend]; if((f = TTF_OpenFont(ofile, ptsz)) == NULL){ fprintf(stderr, "%s: %s\n", argv[0], TTF_GetError()); goto err; } if(dpi > 0) TTF_SetFontSizeDPI(f, ptsz, dpi, dpi); TTF_SetFontStyle(f, style); TTF_SetFontKerning(f, 0); TTF_SetFontHinting(f, hint); if(!hdr){ h = TTF_FontLineSkip(f); dprintf(fdfont, "%-4d %d\n", h*scale, TTF_FontAscent(f)*scale); hdr = 1; } if((ranges = getcharset(ofile, &nr, pjwface)) == NULL) goto err; for(i = 0; i < nr; i++){ rn = ranges+i; w = 0; for(r = rn->min; r <= rn->max; r++){ if(TTF_GlyphMetrics32(f, r, &minx, &maxx, &dummy, &dummy, &advance) == 0){ advance = MAX(maxx-minx, advance); if(w+advance > Maxsubfwidth){ /* subfont too wide - split the range */ ranges = realloc(ranges, (nr+1)*sizeof(Range)); rn = ranges+i; memmove(rn+1, rn, (nr-i)*sizeof(Range)); rn[0].max = r-1; rn[1].min = r; nr++; break; } w += advance; } } if(w == 0){ /* skip zero-width subfonts altogether */ memmove(rn, rn+1, (nr-i-1)*sizeof(Range)); nr--; i--; } } for(i = 0; i < nr; i++){ int ncs, x, y; SDL_Surface *g, *line; Fontchar *cs; SDL_Rect dst; rn = ranges+i; dprintf(fdfont, "0x%X\t0x%X\t%s.%X-%X\n", rn->min, rn->max, prefix, rn->min, rn->max); w = 0; for(r = rn->min; r <= rn->max; r++){ if(TTF_GlyphMetrics32(f, r, &minx, &maxx, &dummy, &dummy, &advance) == 0) w += MAX(maxx-minx, advance); } h = TTF_FontLineSkip(f); line = SDL_CreateRGBSurfaceWithFormat(0, w, h, 32, SDL_PIXELFORMAT_XRGB8888); SDL_FillRect(line, NULL, SDL_MapRGBA(line->format, 0, 0, 0, 0)); memset(&dst, 0, sizeof(dst)); cs = calloc(rn->max - rn->min + 1, sizeof(*cs)); for(r = rn->min, ncs = 0; r <= rn->max; r++, ncs++){ cs[ncs].x = dst.x; if(TTF_GlyphMetrics32(f, r, &minx, &maxx, &dummy, &dummy, &advance) != 0){ fprintf(stderr, "%s: rune 0x%x: %s\n", ofile, r, TTF_GetError()); free(cs); goto err; } if((g = TTF_RenderGlyph32_Shaded(f, r, white, black)) == NULL){ /* zero-width chars end up here */ continue; } // this would make sure glyphs always fit, but it also // makes some monospace fonts not actually have a fixed width //advance = MAX(g->w, MAX(maxx-minx, advance)); if(w < dst.x+advance || h < g->h){ SDL_Surface *bigger; if(w < dst.x+advance) w = dst.x+advance; if(h < g->h) h = g->h; bigger = SDL_CreateRGBSurfaceWithFormat(0, w, h, 32, SDL_PIXELFORMAT_XRGB8888); SDL_FillRect(bigger, NULL, SDL_MapRGBA(bigger->format, 0, 0, 0, 0)); SDL_BlitSurface(line, NULL, bigger, NULL); SDL_FreeSurface(line); line = bigger; } for(y = 0; y < g->h; y++){ for(x = 0; x < g->w; x++){ if(((uint8_t*)g->pixels+y*g->pitch)[x*g->format->BytesPerPixel] != 0){ cs[ncs].top = y; for(y = g->h-1; y >= cs[ncs].top; y--){ for(x = g->w-1; x >= 0; x--){ if(((uint8_t*)g->pixels+y*g->pitch)[x*g->format->BytesPerPixel] != 0){ cs[ncs].bottom = y+1; goto done; } } } } } }done:// if(r == RUNE)// fprintf(stderr, "0x%x is %dx%d, top=%d bottom=%d\n", r, g->w, g->h, cs[ncs].top, cs[ncs].bottom); if(maxh < cs[ncs].bottom) maxh = cs[ncs].bottom; SDL_BlitSurface(g, NULL, line, &dst); cs[ncs].width = advance; dst.x += advance; SDL_FreeSurface(g); } snprintf(s, ssz, "%s.%X-%X", opath, rn->min, rn->max); b = surf2k8(line); if(savesubfont(b, line->w, line->h, TTF_FontAscent(f), scale, cs, ncs, s) != 0) goto err; free(b); free(cs); SDL_FreeSurface(line); } TTF_CloseFont(f); free(ranges); } if(pjwface) pjwit(fdfont, s, ssz, scale); /* actual height of the font might be slightly different, so rewrite it */ lseek(fdfont, 0, 0); dprintf(fdfont, "%-4d", maxh*scale); close(fdfont); if(0){err: res = 1; } free(s); free(prefix); return res;}#endif
#include <stdio.h>
#include <stdlib.h>
#include <stdint.h>
#include <fcntl.h>
#include <unistd.h>
#include <sys/stat.h>
#include <SDL_ttf.h>
#include "parg.h"
#include "common.h"
#if !SDL_TTF_VERSION_ATLEAST(2,0,18)
#error SDL2_ttf of version >= 2.0.18 required
#else
#ifndef MAX
#define MAX(a,b) ((a)>(b)?(a):(b))
#endif
enum {
Substitute = 0xfffd,
Maxsubfwidth = 3000, /* rough */
};
static uint8_t pjw[] = {
#include "pjw.h"
};
static char *opts = "d:hPs:x:H:S:";
static SDL_Color white = {255, 255, 255, 255};
static SDL_Color black = {0, 0, 0, 255};
static char *opath, *prefix;
static int ptsz, hint, style;
static uint8_t *
surf2k8(SDL_Surface *s)
{
uint8_t *b, *p;
int sz, x, y;
sz = s->w*s->h;
b = p = malloc(sz);
for(y = 0; y < s->h; y++){
for(x = 0; x < s->w; x++)
*p++ = ((uint8_t*)s->pixels + y*s->pitch)[x*4];
}
return b;
}
static int
pjwit(int f, char *s, int ssz, int scale)
{
SDL_Surface *g, *line;
int dummy, advance, r;
SDL_RWops *ops;
TTF_Font *pf;
Fontchar fc;
uint8_t *b;
r = 0;
g = NULL;
b = NULL;
line = NULL;
dprintf(f, "0x%X\t0x%X\t%s.%X-%X\n", 0, 0, prefix, 0, 0);
dprintf(f, "0x%X\t0x%X\t%s.%X-%X\n", Substitute, Substitute, prefix, Substitute, Substitute);
if((ops = SDL_RWFromConstMem(pjw, sizeof(pjw))) == NULL){
fprintf(stderr, "pjw: %s\n", SDL_GetError());
return -1;
}
if((pf = TTF_OpenFontRW(ops, 1, ptsz*scale)) == NULL ||
TTF_SetFontSize(pf, ptsz*scale) != 0 ||
TTF_GlyphMetrics32(pf, Substitute, &dummy, &dummy, &dummy, &dummy, &advance) != 0 ||
(g = TTF_RenderGlyph32_Shaded(pf, Substitute, white, black)) == NULL ||
(line = SDL_CreateRGBSurfaceWithFormat(0, g->w, g->h, 32, SDL_PIXELFORMAT_XRGB8888)) == NULL)
goto err;
SDL_FillRect(line, NULL, SDL_MapRGBA(line->format, 0, 0, 0, 0));
SDL_BlitSurface(g, NULL, line, NULL);
b = surf2k8(line);
if(b == NULL)
goto err;
snprintf(s, ssz, "%s.%X-%X", opath, 0, 0);
memset(&fc, 0, sizeof(fc));
fc.width = advance;
fc.bottom = line->h;
if(savesubfont(b, line->w, line->h, TTF_FontAscent(pf), 1, &fc, 1, s) != 0)
goto err2;
snprintf(s, ssz, "%s.%X-%X", opath, Substitute, Substitute);
if(savesubfont(b, line->w, line->h, TTF_FontAscent(pf), 1, &fc, 1, s) != 0)
goto err2;
if(0){
err:
fprintf(stderr, "pjw: %s\n", TTF_GetError());
err2:
r = -1;
}
if(g != NULL)
SDL_FreeSurface(g);
if(line != NULL)
SDL_FreeSurface(line);
if(pf != NULL)
TTF_CloseFont(pf);
else
SDL_RWclose(ops);
free(b);
return r;
}
int
main(int argc, char **argv)
{
int c, optend, nr, i, fdfont, ssz, res, dpi;
int minx, maxx, advance, dummy, w, h, maxh, pjwface, hdr, scale;
struct parg_state ps;
Range *ranges, *rn;
char *ofile, *s;
TTF_Font *f;
uint8_t *b;
Uint32 r;
if(TTF_Init() != 0){
fprintf(stderr, "ttf init failed: %s\n", TTF_GetError());
return 1;
}
dpi = -1;
ptsz = 16;
hint = TTF_HINTING_NORMAL;
style = TTF_STYLE_NORMAL;
pjwface = 1;
scale = 1;
parg_init(&ps);
optend = parg_reorder(argc, argv, opts, NULL);
while((c = parg_getopt(&ps, argc, argv, opts)) >= 0){
switch(c){
case 1:
break;
case 'd':
dpi = atoi(ps.optarg);
break;
case 's':
if((ptsz = atoi(ps.optarg)) < 2){
fprintf(stderr, "invalid point size: %s\n", ps.optarg);
return 1;
}
break;
case 'x':
if((scale = atoi(ps.optarg)) < 1){
fprintf(stderr, "invalid scaling: %s\n", ps.optarg);
return 1;
}
break;
case 'H':
if(strcmp(ps.optarg, "normal") == 0)
hint = TTF_HINTING_NORMAL;
else if(strcmp(ps.optarg, "light") == 0)
hint = TTF_HINTING_LIGHT;
else if(strcmp(ps.optarg, "mono") == 0)
hint = TTF_HINTING_MONO;
else if(strcmp(ps.optarg, "none") == 0)
hint = TTF_HINTING_NONE;
else if(strcmp(ps.optarg, "light_subpixel") == 0)
hint = TTF_HINTING_LIGHT_SUBPIXEL;
else{
fprintf(stderr, "invalid hinting: %s\n", ps.optarg);
return 1;
}
break;
case 'P':
pjwface = 0;
fprintf(stderr, "shame on you!\n");
break;
case 'S':
if(strcmp(ps.optarg, "normal") == 0)
style = TTF_STYLE_NORMAL;
else if(strcmp(ps.optarg, "bold") == 0)
style |= TTF_STYLE_BOLD;
else if(strcmp(ps.optarg, "italic") == 0)
style |= TTF_STYLE_ITALIC;
else if(strcmp(ps.optarg, "underline") == 0)
style |= TTF_STYLE_UNDERLINE;
else if(strcmp(ps.optarg, "strikethrough") == 0)
style |= TTF_STYLE_STRIKETHROUGH;
else{
fprintf(stderr, "invalid style: %s\n", ps.optarg);
return 1;
}
break;
case 'h':
usage:
fprintf(stderr, "usage: %s [-d DPI] [-s point_size] [-x scale] [-H hinting] [-S style [-S ...]] [-P] FILE [FILE ...] PATH/PREFIX\n", argv[0]);
fprintf(stderr, " -H: normal (default), none, light, mono, light_subpixel\n");
fprintf(stderr, " -S: normal (default), bold, italic, underline, strikethrough\n");
fprintf(stderr, " -P: I don't want PJW face in my font (shame on you)\n");
return 1;
case '?':
fprintf(stderr, "unknown option -%c\n", ps.optopt);
return 1;
default:
fprintf(stderr, "unhandled option -%c\n", c);
return 1;
}
}
if(argc-optend < 2)
goto usage;
opath = argv[argc-1];
i = strlen(opath);
s = malloc(ssz = i+64);
snprintf(s, ssz, "%s.font", opath);
if((prefix = strrchr(s, '/')) == NULL)
prefix = s;
else{
*prefix++ = 0;
mkdir(s, 0755);
prefix[-1] = '/';
}
res = 0;
if((fdfont = open(s, O_CREAT|O_TRUNC|O_WRONLY|O_CLOEXEC, 0644)) < 0){
prefix = NULL;
perror(s);
goto err;
}
s[i] = 0;
prefix = strdup(prefix);
maxh = 0;
for(hdr = 0; optend < argc-1; optend++){
ofile = argv[optend];
if((f = TTF_OpenFont(ofile, ptsz)) == NULL){
fprintf(stderr, "%s: %s\n", argv[0], TTF_GetError());
goto err;
}
if(dpi > 0)
TTF_SetFontSizeDPI(f, ptsz, dpi, dpi);
TTF_SetFontStyle(f, style);
TTF_SetFontKerning(f, 0);
TTF_SetFontHinting(f, hint);
if(!hdr){
h = TTF_FontLineSkip(f);
dprintf(fdfont, "%-4d %d\n", h*scale, TTF_FontAscent(f)*scale);
hdr = 1;
}
if((ranges = getcharset(ofile, &nr, pjwface)) == NULL)
goto err;
for(i = 0; i < nr; i++){
rn = ranges+i;
w = 0;
for(r = rn->min; r <= rn->max; r++){
if(TTF_GlyphMetrics32(f, r, &minx, &maxx, &dummy, &dummy, &advance) == 0){
advance = MAX(maxx-minx, advance);
if(w+advance > Maxsubfwidth){
/* subfont too wide - split the range */
ranges = realloc(ranges, (nr+1)*sizeof(Range));
rn = ranges+i;
memmove(rn+1, rn, (nr-i)*sizeof(Range));
rn[0].max = r-1;
rn[1].min = r;
nr++;
break;
}
w += advance;
}
}
if(w == 0){
/* skip zero-width subfonts altogether */
memmove(rn, rn+1, (nr-i-1)*sizeof(Range));
nr--;
i--;
}
}
for(i = 0; i < nr; i++){
int ncs, x, y;
SDL_Surface *g, *line;
Fontchar *cs;
SDL_Rect dst;
rn = ranges+i;
dprintf(fdfont, "0x%X\t0x%X\t%s.%X-%X\n", rn->min, rn->max, prefix, rn->min, rn->max);
w = 0;
for(r = rn->min; r <= rn->max; r++){
if(TTF_GlyphMetrics32(f, r, &minx, &maxx, &dummy, &dummy, &advance) == 0)
w += MAX(maxx-minx, advance);
}
h = TTF_FontLineSkip(f);
line = SDL_CreateRGBSurfaceWithFormat(0, w, h, 32, SDL_PIXELFORMAT_XRGB8888);
SDL_FillRect(line, NULL, SDL_MapRGBA(line->format, 0, 0, 0, 0));
memset(&dst, 0, sizeof(dst));
cs = calloc(rn->max - rn->min + 1, sizeof(*cs));
for(r = rn->min, ncs = 0; r <= rn->max; r++, ncs++){
cs[ncs].x = dst.x;
if(TTF_GlyphMetrics32(f, r, &minx, &maxx, &dummy, &dummy, &advance) != 0){
fprintf(stderr, "%s: rune 0x%x: %s\n", ofile, r, TTF_GetError());
free(cs);
goto err;
}
if((g = TTF_RenderGlyph32_Shaded(f, r, white, black)) == NULL){
/* zero-width chars end up here */
continue;
}
// this would make sure glyphs always fit, but it also
// makes some monospace fonts not actually have a fixed width
//advance = MAX(g->w, MAX(maxx-minx, advance));
if(w < dst.x+advance || h < g->h){
SDL_Surface *bigger;
if(w < dst.x+advance)
w = dst.x+advance;
if(h < g->h)
h = g->h;
bigger = SDL_CreateRGBSurfaceWithFormat(0, w, h, 32, SDL_PIXELFORMAT_XRGB8888);
SDL_FillRect(bigger, NULL, SDL_MapRGBA(bigger->format, 0, 0, 0, 0));
SDL_BlitSurface(line, NULL, bigger, NULL);
SDL_FreeSurface(line);
line = bigger;
}
for(y = 0; y < g->h; y++){
for(x = 0; x < g->w; x++){
if(((uint8_t*)g->pixels+y*g->pitch)[x*g->format->BytesPerPixel] != 0){
cs[ncs].top = y;
for(y = g->h-1; y >= cs[ncs].top; y--){
for(x = g->w-1; x >= 0; x--){
if(((uint8_t*)g->pixels+y*g->pitch)[x*g->format->BytesPerPixel] != 0){
cs[ncs].bottom = y+1;
goto done;
}
}
}
}
}
}
done:
// if(r == RUNE)
// fprintf(stderr, "0x%x is %dx%d, top=%d bottom=%d\n", r, g->w, g->h, cs[ncs].top, cs[ncs].bottom);
if(maxh < cs[ncs].bottom)
maxh = cs[ncs].bottom;
SDL_BlitSurface(g, NULL, line, &dst);
cs[ncs].width = advance;
dst.x += advance;
SDL_FreeSurface(g);
}
snprintf(s, ssz, "%s.%X-%X", opath, rn->min, rn->max);
b = surf2k8(line);
if(savesubfont(b, line->w, line->h, TTF_FontAscent(f), scale, cs, ncs, s) != 0)
goto err;
free(b);
free(cs);
SDL_FreeSurface(line);
}
TTF_CloseFont(f);
free(ranges);
}
if(pjwface)
pjwit(fdfont, s, ssz, scale);
/* actual height of the font might be slightly different, so rewrite it */
lseek(fdfont, 0, 0);
dprintf(fdfont, "%-4d", maxh*scale);
close(fdfont);
if(0){
err:
res = 1;
}
free(s);
free(prefix);
return res;
}
#endif