1 /* -*- c-basic-offset:2; tab-width:2; indent-tabs-mode:nil -*- */
2 
3 #include "ui_font_config.h"
4 
5 #include <string.h> /* memset */
6 
7 #include <pobl/bl_mem.h>  /* malloc */
8 #include <pobl/bl_str.h>  /* strdup */
9 #include <pobl/bl_util.h> /* DIGIT_STR_LEN */
10 #include <pobl/bl_conf_io.h>
11 #include <pobl/bl_file.h>
12 #include <pobl/bl_debug.h>
13 
14 #include <vt_char.h>
15 #include <vt_char_encoding.h> /* vt_parse_unicode_area */
16 
17 #define DEFAULT_FONT 0x1ff /* MAX_CHARSET */
18 
19 #if 0
20 #define __DEBUG
21 #endif
22 
23 typedef struct cs_table {
24   char *name;
25   ef_charset_t cs;
26 
27 } cs_table_t;
28 
29 typedef struct custom_cache {
30   const char *file;
31   char *key;
32   char *value;
33 
34 } custom_cache_t;
35 
36 /* --- static variables --- */
37 
38 #if defined(USE_FRAMEBUFFER) || defined(USE_CONSOLE) || defined(USE_WAYLAND) || defined(USE_SDL2)
39 
40 #if defined(USE_FREETYPE) && defined(USE_FONTCONFIG)
41 static int use_aafont;
42 #define FONT_FILE (use_aafont ? aafont_file + 7 : "font")
43 #define VFONT_FILE (use_aafont ? vaafont_file + 7 : "vfont")
44 #define TFONT_FILE (use_aafont ? taafont_file + 7 : "tfont")
45 #else
46 #define FONT_FILE "font"
47 #define VFONT_FILE "vfont"
48 #define TFONT_FILE "tfont"
49 #endif
50 
51 static char *font_file = "mlterm/font-fb";
52 static char *vfont_file = "mlterm/vfont-fb";
53 static char *tfont_file = "mlterm/tfont-fb";
54 
55 #else
56 
57 #define FONT_FILE (font_file + 7)
58 #define VFONT_FILE (vfont_file + 7)
59 #define TFONT_FILE (tfont_file + 7)
60 static char *font_file = "mlterm/font";
61 static char *vfont_file = "mlterm/vfont";
62 static char *tfont_file = "mlterm/tfont";
63 
64 #endif
65 
66 static char *aafont_file = "mlterm/aafont";
67 static char *vaafont_file = "mlterm/vaafont";
68 static char *taafont_file = "mlterm/taafont";
69 
70 /*
71  * If this table is changed, ui_font.c:cs_info_table and mc_font.c:cs_info_table
72  * shoule be also changed.
73  */
74 static cs_table_t cs_table[] = {
75     {"ISO10646_UCS4_1", ISO10646_UCS4_1},
76 
77     {"DEC_SPECIAL", DEC_SPECIAL},
78     {"ISO8859_1", ISO8859_1_R},
79     {"ISO8859_2", ISO8859_2_R},
80     {"ISO8859_3", ISO8859_3_R},
81     {"ISO8859_4", ISO8859_4_R},
82     {"ISO8859_5", ISO8859_5_R},
83     {"ISO8859_6", ISO8859_6_R},
84     {"ISO8859_7", ISO8859_7_R},
85     {"ISO8859_8", ISO8859_8_R},
86     {"ISO8859_9", ISO8859_9_R},
87     {"ISO8859_10", ISO8859_10_R},
88     {"TIS620", TIS620_2533},
89     {"ISO8859_13", ISO8859_13_R},
90     {"ISO8859_14", ISO8859_14_R},
91     {"ISO8859_15", ISO8859_15_R},
92     {"ISO8859_16", ISO8859_16_R},
93     {"TCVN5712", TCVN5712_3_1993},
94     {"ISCII_ASSAMESE", ISCII_ASSAMESE},
95     {"ISCII_BENGALI", ISCII_BENGALI},
96     {"ISCII_GUJARATI", ISCII_GUJARATI},
97     {"ISCII_HINDI", ISCII_HINDI},
98     {"ISCII_KANNADA", ISCII_KANNADA},
99     {"ISCII_MALAYALAM", ISCII_MALAYALAM},
100     {"ISCII_ORIYA", ISCII_ORIYA},
101     {"ISCII_PUNJABI", ISCII_PUNJABI},
102     {"ISCII_TAMIL", ISCII_TAMIL},
103     {"ISCII_TELUGU", ISCII_TELUGU},
104     {"VISCII", VISCII},
105     {"KOI8_R", KOI8_R},
106     {"KOI8_U", KOI8_U},
107 #if 0
108     /*
109      * Koi8_t and georgian_ps charsets can be shown by unicode font only.
110      */
111     {"KOI8_T", KOI8_T},
112     {"GEORGIAN_PS", GEORGIAN_PS},
113 #endif
114 #ifdef USE_WIN32GUI
115     {"CP1250", CP1250},
116     {"CP1251", CP1251},
117     {"CP1252", CP1252},
118     {"CP1253", CP1253},
119     {"CP1254", CP1254},
120     {"CP1255", CP1255},
121     {"CP1256", CP1256},
122     {"CP1257", CP1257},
123     {"CP1258", CP1258},
124 #endif
125     {"JISX0201_KATA", JISX0201_KATA},
126     {"JISX0201_ROMAN", JISX0201_ROMAN},
127     {"JISX0208_1978", JISC6226_1978},
128     {"JISC6226_1978", JISC6226_1978},
129     {"JISX0208_1983", JISX0208_1983},
130     {"JISX0208_1990", JISX0208_1990},
131     {"JISX0212_1990", JISX0212_1990},
132     {"JISX0213_2000_1", JISX0213_2000_1},
133     {"JISX0213_2000_2", JISX0213_2000_2},
134     {"KSC5601_1987", KSC5601_1987},
135     {"KSX1001_1997", KSC5601_1987},
136 #if 0
137     /*
138      * XXX
139      * UHC and JOHAB fonts are not used at the present time.
140      * see vt_vt100_parser.c:vt_parse_vt100_sequence().
141      */
142     {"UHC", UHC},
143     {"JOHAB", JOHAB},
144 #endif
145     {"GB2312_80", GB2312_80},
146     {"GBK", GBK},
147     {"BIG5", BIG5},
148     {"HKSCS", HKSCS},
149     {"CNS11643_1992_1", CNS11643_1992_1},
150     {"CNS11643_1992_2", CNS11643_1992_2},
151     {"CNS11643_1992_3", CNS11643_1992_3},
152     {"CNS11643_1992_4", CNS11643_1992_4},
153     {"CNS11643_1992_5", CNS11643_1992_5},
154     {"CNS11643_1992_6", CNS11643_1992_6},
155     {"CNS11643_1992_7", CNS11643_1992_7},
156 
157 };
158 
159 static ui_font_config_t **font_configs;
160 static u_int num_configs;
161 
162 /*
163  * These will be leaked unless change_custom_cache( ... , "") deletes them.
164  * change_custom_cache( ... , "") is called only from save_conf, which means
165  * that they are deleted when all of them are saved to ~/.mlterm/(vt)(aa)font
166  * file.
167  */
168 static custom_cache_t *custom_cache;
169 static u_int num_customs;
170 
171 /* --- static functions --- */
172 
get_font_name_pair(BL_MAP (ui_font_name)table,vt_font_t font)173 static BL_PAIR(ui_font_name) get_font_name_pair(BL_MAP(ui_font_name) table, vt_font_t font) {
174   BL_PAIR(ui_font_name) pair;
175 
176   bl_map_get(table, font, pair);
177 
178   return pair;
179 }
180 
BL_PAIR(ui_font_name)181 static BL_PAIR(ui_font_name) * get_font_name_pairs_array(u_int *size, BL_MAP(ui_font_name) table) {
182   BL_PAIR(ui_font_name) * array;
183 
184   bl_map_get_pairs_array(table, array, *size);
185 
186   return array;
187 }
188 
set_font_name_to_table(BL_MAP (ui_font_name)table,vt_font_t font,char * fontname)189 static int set_font_name_to_table(BL_MAP(ui_font_name) table, vt_font_t font, char *fontname) {
190   int result;
191 
192   bl_map_set(result, table, font, fontname);
193 
194   return result;
195 }
196 
parse_key(const char * key)197 static vt_font_t parse_key(const char *key) {
198   int count;
199   size_t key_len;
200   ef_charset_t cs;
201   vt_font_t font;
202 
203   key_len = strlen(key);
204 
205   if (key_len >= 7 && strncmp(key, "DEFAULT", 7) == 0) {
206     if (key_len >= 8) {
207       bl_warn_printf("Illegal charset for font: %s.\n", key);
208       return UNKNOWN_CS;
209     }
210 
211     font = DEFAULT_FONT;
212 
213     goto check_style;
214   }
215 
216   if (key_len >= 3 && strncmp(key, "U+", 2) == 0) {
217     u_int min;
218     u_int max;
219 
220     if (vt_parse_unicode_area(key, &min, &max) &&
221         (font = vt_get_unicode_area_font(min, max)) != UNKNOWN_CS) {
222       goto check_style;
223     } else {
224       return UNKNOWN_CS;
225     }
226   }
227 
228   for (count = 0; count < sizeof(cs_table) / sizeof(cs_table[0]); count++) {
229     size_t nlen;
230 
231     nlen = strlen(cs_table[count].name);
232 
233     if (key_len >= nlen && strncmp(cs_table[count].name, key, nlen) == 0 &&
234         (key[nlen] == '\0' ||
235          /* "_BOLD" or "_FULLWIDTH" is trailing */ key[nlen] == '_')) {
236       cs = cs_table[count].cs;
237 
238       break;
239     }
240   }
241 
242   if (count == sizeof(cs_table) / sizeof(cs_table[0])) {
243 #ifdef DEBUG
244     bl_debug_printf(BL_DEBUG_TAG " %s is not valid charset.\n", key);
245 #endif
246 
247     return UNKNOWN_CS;
248   }
249 
250   font = NORMAL_FONT_OF(cs);
251 
252   if (!(font & FONT_FULLWIDTH) && (strstr(key, "_BIWIDTH") || /* XXX compat with 3.2.2 or before. */
253                                    strstr(key, "_FULLWIDTH"))) {
254     font |= FONT_FULLWIDTH;
255   }
256 
257 check_style:
258   if (strstr(key, "_BOLD")) {
259     font |= FONT_BOLD;
260   }
261 
262   if (strstr(key, "_ITALIC")) {
263     font |= FONT_ITALIC;
264   }
265 
266   return font;
267 }
268 
parse_value(char ** font_name,char * value)269 static void parse_value(char **font_name, /* if value is "" or illegal format, not changed. */
270                         char *value       /* Don't specify NULL. */ ) {
271 #if 1 /* XXX Compat with old format (3.6.3 or before): [size],[font name] */
272   char *p;
273 
274   if ('0' <= *value && *value <= '9' && (p = strchr(value, ','))) {
275     value = p + 1;
276   }
277 #endif
278 
279   *font_name = value;
280 }
281 
282 /*
283  * <Return value>
284  * 1: Valid "%d" or no '%' is found.
285  * 0: Invalid '%' is found.
286  */
is_valid_font_format(const char * format)287 static int is_valid_font_format(const char *format) {
288   char *p;
289 
290   if ((p = strchr(format, '%'))) {
291     /* force to be '%d' */
292     if (p[1] != 'd') {
293       return 0;
294     }
295 
296     /* '%' can happen only once at most */
297     if (p != strrchr(format, '%')) {
298       return 0;
299     }
300   }
301 
302   return 1;
303 }
304 
305 /*
306  * <Return value>
307  * 0: Not changed(including the case of failure).
308  * 1: Succeeded.
309  */
customize_font_name(ui_font_config_t * font_config,vt_font_t font,const char * fontname)310 static int customize_font_name(ui_font_config_t *font_config, vt_font_t font,
311                                const char *fontname) {
312   BL_PAIR(ui_font_name) pair;
313 
314   if (is_valid_font_format(fontname) == 0) {
315     bl_msg_printf("%s is invalid format for font name.\n");
316 
317     return 0;
318   }
319 
320   if ((pair = get_font_name_pair(font_config->font_name_table, font))) {
321     if (*fontname == '\0') {
322       int result;
323 
324       /* Curent setting in font_config is removed. */
325       free(pair->value);
326       bl_map_erase_simple(result, font_config->font_name_table, font);
327     } else if (strcmp(pair->value, fontname) != 0) {
328       char *value;
329 
330       if ((value = strdup(fontname)) == NULL) {
331         return 0;
332       }
333 
334       free(pair->value);
335       pair->value = value;
336     } else {
337       /* If new fontname is the same as current one, nothing is done. */
338       return 0;
339     }
340   } else {
341     char *value;
342 
343     if (*fontname == '\0' || (value = strdup(fontname)) == NULL) {
344       return 0;
345     }
346 
347     set_font_name_to_table(font_config->font_name_table, font, value);
348   }
349 
350 #ifdef DEBUG
351   bl_debug_printf(BL_DEBUG_TAG " Set %x font => fontname %s.\n", font, fontname);
352 #endif
353 
354   return 1;
355 }
356 
parse_conf(ui_font_config_t * font_config,const char * key,const char * value)357 static int parse_conf(ui_font_config_t *font_config, const char *key,
358                       const char *value /* value = "" or ";" => Reset font name. */
359                       ) {
360   vt_font_t font;
361   char *font_name;
362 
363   if ((font = parse_key(key)) == UNKNOWN_CS ||
364       (font_name = alloca(strlen(value) + 1)) == NULL) {
365     return 0;
366   }
367   strcpy(font_name, value);
368 
369   /*
370    * XXX
371    * Compat with old formats (3.6.3 or before): [default font name];[font size],[font name];...
372    */
373 #if 1
374   {
375     const char *p = value;
376     while ((p = strchr(p, ';'))) {
377       p++;
378       if (('0' <= *p && *p <= '9') || *p == '\0') {
379         font_name[p - value - 1] = '\0';
380 
381         break;
382       }
383     }
384   }
385 #endif
386 
387   parse_value(&font_name, font_name);
388   customize_font_name(font_config, font, font_name);
389 
390   return 1;
391 }
392 
apply_custom_cache(ui_font_config_t * font_config,const char * filename)393 static int apply_custom_cache(ui_font_config_t *font_config, const char *filename) {
394   u_int count;
395 
396   for (count = 0; count < num_customs; count++) {
397     if (filename == custom_cache[count].file) {
398 #ifdef __DEBUG
399       bl_debug_printf("Appling customization %s=%s\n", custom_cache[count].key,
400                       custom_cache[count].value);
401 #endif
402 
403       parse_conf(font_config, custom_cache[count].key, custom_cache[count].value);
404     }
405   }
406 
407   return 1;
408 }
409 
read_conf(ui_font_config_t * font_config,const char * filename)410 static int read_conf(ui_font_config_t *font_config, const char *filename) {
411   bl_file_t *from;
412   char *key;
413   char *value;
414 
415 #ifdef __DEBUG
416   bl_debug_printf(BL_DEBUG_TAG " read_conf( %s)\n", filename);
417 #endif
418 
419   if (!(from = bl_file_open(filename, "r"))) {
420 #ifdef DEBUG
421     bl_warn_printf(BL_DEBUG_TAG " %s couldn't be opened.\n", filename);
422 #endif
423 
424     return 0;
425   }
426 
427   while (bl_conf_io_read(from, &key, &value)) {
428 #ifdef __DEBUG
429     bl_debug_printf(BL_DEBUG_TAG " Read line from %s => %s = %s\n", filename, key, value);
430 #endif
431 
432     parse_conf(font_config, key, value);
433   }
434 
435   bl_file_close(from);
436 
437   return 1;
438 }
439 
read_all_conf(ui_font_config_t * font_config,const char * changed_font_file)440 static int read_all_conf(ui_font_config_t *font_config,
441                          const char *changed_font_file /* If this function is
442                                                         * called after a font
443                                                         * file is
444                                                         * changed, specify it
445                                                         * here to avoid re-read
446                                                         * font files.
447                                                         * Otherwise specify
448                                                         * NULL. */
449                          ) {
450   char *font_rcfile;
451   char *font_rcfile2; /* prior to font_rcfile */
452   char *rcpath;
453 
454 #if defined(USE_FREETYPE) && defined(USE_FONTCONFIG)
455   if (use_aafont)
456 #else
457   /* '>= XFT' means XFT or Cairo */
458   if (font_config->type_engine >= TYPE_XFT)
459 #endif
460   {
461     font_rcfile = aafont_file;
462 
463     switch (font_config->font_present & ~FONT_AA) {
464       default:
465         font_rcfile2 = NULL;
466         break;
467 
468       case FONT_VAR_WIDTH:
469         font_rcfile2 = vaafont_file;
470         break;
471 
472       case FONT_VERTICAL:
473         font_rcfile2 = taafont_file;
474         break;
475     }
476   } else {
477     font_rcfile = font_file;
478 
479     switch (font_config->font_present & ~FONT_AA) {
480       default:
481         font_rcfile2 = NULL;
482         break;
483 
484       case FONT_VAR_WIDTH:
485         font_rcfile2 = vfont_file;
486         break;
487 
488       case FONT_VERTICAL:
489         font_rcfile2 = tfont_file;
490         break;
491     }
492   }
493 
494   if (!changed_font_file) {
495     if ((rcpath = bl_get_sys_rc_path(font_rcfile))) {
496       read_conf(font_config, rcpath);
497       free(rcpath);
498     }
499   }
500 
501   if (!changed_font_file || changed_font_file == font_rcfile) {
502     if ((rcpath = bl_get_user_rc_path(font_rcfile))) {
503       read_conf(font_config, rcpath);
504       free(rcpath);
505     }
506   }
507 
508   apply_custom_cache(font_config, font_rcfile);
509 
510   if (font_rcfile2) {
511     if (!changed_font_file) {
512       if ((rcpath = bl_get_sys_rc_path(font_rcfile2))) {
513         read_conf(font_config, rcpath);
514         free(rcpath);
515       }
516     }
517 
518     if ((rcpath = bl_get_user_rc_path(font_rcfile2))) {
519       read_conf(font_config, rcpath);
520       free(rcpath);
521     }
522 
523     apply_custom_cache(font_config, font_rcfile2);
524   }
525 
526   return 1;
527 }
528 
change_custom_cache(const char * file,const char * key,const char * value)529 static int change_custom_cache(const char *file, const char *key, const char *value) {
530   void *p;
531   u_int count;
532 
533   for (count = 0; count < num_customs; count++) {
534     if (custom_cache[count].file == file && strcmp(custom_cache[count].key, key) == 0) {
535       if (*value) {
536         /* replace */
537         char *p;
538 
539         if (strcmp(custom_cache[count].value, value) == 0) {
540           /* not changed */
541           return 0;
542         }
543 
544         if ((p = strdup(value))) {
545           free(custom_cache[count].value);
546           custom_cache[count].value = p;
547         }
548       } else {
549         /* remove */
550 
551         free(custom_cache[count].key);
552         free(custom_cache[count].value);
553         custom_cache[count] = custom_cache[--num_customs];
554         if (num_customs == 0) {
555           free(custom_cache);
556           custom_cache = NULL;
557 
558 #ifdef __DEBUG
559           bl_debug_printf("Custom cache is completely freed.\n");
560 #endif
561         }
562       }
563 
564       return 1;
565     }
566   }
567 
568   /* #if 1 => Don't remove font settings read from *font files in ~/.mlterm */
569 #if 0
570   if (*value == '\0') {
571     return 0;
572   }
573 #endif
574 
575   if ((p = realloc(custom_cache, sizeof(custom_cache_t) * (num_customs + 1))) == NULL) {
576     return 0;
577   }
578 
579   custom_cache = p;
580 
581   if ((custom_cache[num_customs].key = strdup(key)) == NULL) {
582     return 0;
583   }
584 
585   if ((custom_cache[num_customs].value = strdup(value)) == NULL) {
586     free(custom_cache[num_customs].key);
587 
588     return 0;
589   }
590 
591   custom_cache[num_customs++].file = file;
592 
593 #ifdef __DEBUG
594   bl_debug_printf("%s=%s is newly added to custom cache.\n", key, value);
595 #endif
596 
597   return 1;
598 }
599 
write_conf(char * path,const char * key,const char * value)600 static int write_conf(char *path, /* Can be destroyed in this function. */
601                       const char *key, const char *value) {
602   bl_conf_write_t *conf;
603 
604   if (!(conf = bl_conf_write_open(path))) {
605     return 0;
606   }
607 
608   bl_conf_io_write(conf, key, value);
609 
610   bl_conf_write_close(conf);
611 
612   return 1;
613 }
614 
save_conf(const char * file,const char * key,char * value)615 static int save_conf(const char *file, const char *key,
616                      char *value /* Includes multiple entries. Destroyed in this function. */
617                      ) {
618   char *path;
619   int ret;
620 
621   if ((path = bl_get_user_rc_path(file)) == NULL) {
622     return 0;
623   }
624 
625   if ((ret = write_conf(path, key, value))) {
626     /* Remove from custom_cache */
627     change_custom_cache(file, key, "");
628   }
629 
630   free(path);
631 
632   return ret;
633 }
634 
find_font_config(ui_type_engine_t type_engine,ui_font_present_t font_present)635 static ui_font_config_t *find_font_config(ui_type_engine_t type_engine,
636                                           ui_font_present_t font_present) {
637   if (font_configs) {
638     u_int count;
639 
640     for (count = 0; count < num_configs; count++) {
641       if (font_configs[count]->font_present == font_present &&
642           font_configs[count]->type_engine == type_engine) {
643         return font_configs[count];
644       }
645     }
646   }
647 
648   return NULL;
649 }
650 
match_font_configs(ui_font_config_t ** matched_configs,u_int max_size,int is_xcore,ui_font_present_t present_mask)651 static u_int match_font_configs(ui_font_config_t **matched_configs,
652                                 u_int max_size, /* must be over 0. */
653                                 int is_xcore, ui_font_present_t present_mask) {
654   u_int count;
655   u_int size;
656 
657   size = 0;
658   for (count = 0; count < num_configs; count++) {
659     if (
660 #if !defined(USE_FREETYPE) || !defined(USE_FONTCONFIG)
661         (is_xcore ? font_configs[count]->type_engine == TYPE_XCORE :
662                     /* '>= XFT' means XFT or Cairo */
663                     font_configs[count]->type_engine >= TYPE_XFT) &&
664 #endif
665         (present_mask ? (font_configs[count]->font_present & present_mask) : 1)) {
666       matched_configs[size++] = font_configs[count];
667       if (size >= max_size) {
668         break;
669       }
670     }
671   }
672 
673   return size;
674 }
675 
create_shared_font_config(ui_type_engine_t type_engine,ui_font_present_t font_present)676 static ui_font_config_t *create_shared_font_config(ui_type_engine_t type_engine,
677                                                    ui_font_present_t font_present) {
678   u_int count;
679 
680   for (count = 0; count < num_configs; count++) {
681     if ((type_engine == TYPE_XCORE ? font_configs[count]->type_engine == TYPE_XCORE :
682                                    /* '>= XFT' means XFT or Cairo */
683              font_configs[count]->type_engine >= TYPE_XFT) &&
684         ((font_configs[count]->font_present & ~FONT_AA) == (font_present & ~FONT_AA))) {
685 #ifdef __DEBUG
686       bl_debug_printf(BL_DEBUG_TAG " Found sharable font_config.\n");
687 #endif
688 
689       ui_font_config_t *font_config;
690 
691       if ((font_config = malloc(sizeof(ui_font_config_t))) == NULL) {
692         return NULL;
693       }
694 
695       font_config->type_engine = type_engine;
696       font_config->font_present = font_present;
697       font_config->font_name_table = font_configs[count]->font_name_table;
698       font_config->ref_count = 0;
699 
700       return font_config;
701     }
702   }
703 
704   return NULL;
705 }
706 
new_table(ui_font_config_t * font_config)707 static void new_table(ui_font_config_t *font_config) {
708   bl_map_new_with_size(vt_font_t, char *, font_config->font_name_table, bl_map_hash_int,
709                        bl_map_compare_int, 16);
710 }
711 
destroy_table(ui_font_config_t * font_config)712 static void destroy_table(ui_font_config_t *font_config) {
713   u_int count;
714   u_int size;
715   BL_PAIR(ui_font_name) * fn_array;
716 
717   fn_array = get_font_name_pairs_array(&size, font_config->font_name_table);
718 
719   for (count = 0; count < size; count++) {
720     free(fn_array[count]->value);
721   }
722 
723   bl_map_destroy(font_config->font_name_table);
724 }
725 
726 /* --- global functions --- */
727 
728 #if defined(USE_FREETYPE) && defined(USE_FONTCONFIG)
ui_use_aafont(void)729 int ui_use_aafont(void) {
730   if (num_configs > 0 || use_aafont) {
731     return 0;
732   }
733 
734   use_aafont = 1;
735   ui_font_use_fontconfig();
736 
737   return 1;
738 }
739 
ui_is_using_aafont(void)740 int ui_is_using_aafont(void) { return use_aafont; }
741 #endif
742 
ui_acquire_font_config(ui_type_engine_t type_engine,ui_font_present_t font_present)743 ui_font_config_t *ui_acquire_font_config(ui_type_engine_t type_engine,
744                                          ui_font_present_t font_present) {
745   ui_font_config_t *font_config;
746   void *p;
747 
748   if ((font_config = find_font_config(type_engine, font_present))) {
749     font_config->ref_count++;
750 
751     return font_config;
752   }
753 
754   if ((p = realloc(font_configs, sizeof(ui_font_config_t *) * (num_configs + 1))) == NULL) {
755     return NULL;
756   }
757 
758   font_configs = p;
759 
760   if ((font_config = create_shared_font_config(type_engine, font_present)) == NULL) {
761     if ((font_config = ui_font_config_new(type_engine, font_present)) == NULL ||
762         !read_all_conf(font_config, NULL)) {
763       return NULL;
764     }
765   }
766 
767   font_config->ref_count++;
768 
769   return font_configs[num_configs++] = font_config;
770 }
771 
ui_release_font_config(ui_font_config_t * font_config)772 void ui_release_font_config(ui_font_config_t *font_config) {
773   u_int count;
774   int has_share;
775   int found;
776 
777   if (--font_config->ref_count > 0) {
778     return;
779   }
780 
781   has_share = 0;
782   found = 0;
783   count = 0;
784   while (count < num_configs) {
785     if (font_configs[count] == font_config) {
786       font_configs[count] = font_configs[--num_configs];
787       found = 1;
788 
789       continue;
790     } else if ((font_config->type_engine == TYPE_XCORE
791                     ? font_configs[count]->type_engine == TYPE_XCORE
792                     :
793                     /* '>= XFT' means XFT or Cairo */
794                     font_configs[count]->type_engine >= TYPE_XFT) &&
795                ((font_configs[count]->font_present & ~FONT_AA) ==
796                 (font_config->font_present & ~FONT_AA))) {
797       has_share = 1;
798     }
799 
800     count++;
801   }
802 
803   if (!found) {
804 #ifdef DEBUG
805     bl_debug_printf(BL_DEBUG_TAG " font_config is not found in font_configs.\n");
806 #endif
807 
808     return;
809   }
810 
811   if (has_share /* && num_configs > 0 */) {
812 #ifdef __DEBUG
813     bl_debug_printf(BL_DEBUG_TAG " Sharable font_config exists.\n");
814 #endif
815 
816     free(font_config);
817 
818     return;
819   }
820 
821   ui_font_config_destroy(font_config);
822 
823   if (num_configs == 0) {
824     free(font_configs);
825     font_configs = NULL;
826   }
827 }
828 
ui_font_config_new(ui_type_engine_t type_engine,ui_font_present_t font_present)829 ui_font_config_t *ui_font_config_new(ui_type_engine_t type_engine, ui_font_present_t font_present) {
830   ui_font_config_t *font_config;
831 
832   if ((font_config = malloc(sizeof(ui_font_config_t))) == NULL) {
833     return NULL;
834   }
835 
836   new_table(font_config);
837 
838   font_config->type_engine = type_engine;
839   font_config->font_present = font_present;
840   font_config->ref_count = 0;
841 
842   return font_config;
843 }
844 
ui_font_config_destroy(ui_font_config_t * font_config)845 void ui_font_config_destroy(ui_font_config_t *font_config) {
846   destroy_table(font_config);
847   free(font_config);
848 }
849 
850 /*
851  * 0 => customization is failed.
852  * -1 => customization is succeeded but saving is failed.
853  * 1 => succeeded.
854  */
ui_customize_font_file(const char * file,char * key,char * value,int save)855 int ui_customize_font_file(const char *file, /* if null, use "mlterm/font" file. */
856                            char *key,        /* charset name */
857                            char *value,      /* font list */
858                            int save) {
859   /*
860    * Max number of target font_config is 6.
861    * [file == aafont_file]
862    * TYPE_XFT, TYPE_XFT & FONT_VAR_WIDTH , TYPE_XFT & FONT_VERTICAL , TYPE_XFT &
863    * FONT_AA ,
864    * TYPE_XFT & FONT_VAR_WIDTH & FONT_AA , TYPE_XFT & FONT_VERTICAL & FONT_AA
865    */
866   ui_font_config_t *targets[6];
867   u_int num_targets;
868   u_int count;
869   int ret;
870 
871   if (file == NULL ||
872 #if defined(USE_FREETYPE) && defined(USE_FONTCONFIG)
873       strcmp(file, "font") == 0
874 #else
875       strcmp(file, FONT_FILE) == 0
876 #endif
877       ) {
878     file = font_file;
879     num_targets = match_font_configs(targets, 6, /* is xcore */ 1, 0);
880   } else if (strcmp(file, aafont_file + 7) == 0) {
881     file = aafont_file;
882     num_targets = match_font_configs(targets, 6, /* is not xcore */ 0, 0);
883   } else if (strcmp(file, VFONT_FILE) == 0) {
884     file = vfont_file;
885     num_targets = match_font_configs(targets, 6, /* is xcore */ 1, FONT_VAR_WIDTH);
886   } else if (strcmp(file, TFONT_FILE) == 0) {
887     file = tfont_file;
888     num_targets = match_font_configs(targets, 6, /* is xcore */ 1, FONT_VERTICAL);
889   } else if (strcmp(file, vaafont_file + 7) == 0) {
890     file = vaafont_file;
891     num_targets = match_font_configs(targets, 6, /* is not xcore */ 0, FONT_VAR_WIDTH);
892   } else if (strcmp(file, taafont_file + 7) == 0) {
893     file = taafont_file;
894     num_targets = match_font_configs(targets, 6, /* is not xcore */ 0, FONT_VERTICAL);
895   } else {
896 #ifdef DEBUG
897     bl_debug_printf(BL_DEBUG_TAG " font file %s is not found.\n", file);
898 #endif
899 
900     return 0;
901   }
902 
903 #ifdef __DEBUG
904   if (num_targets) {
905     bl_debug_printf("customize font file %s %s %s\n", file, key, value);
906   } else {
907     bl_debug_printf("customize font file %s %s %s(not changed in run time)\n", file, key, value);
908   }
909 #endif
910 
911   if (change_custom_cache(file, key, value)) {
912     if (*value == '\0') {
913       /* remove */
914       for (count = 0; count < num_targets; count++) {
915         /*
916          * reset font_name_table
917          *
918          * ~/.mlterm/font: ISO10646_UCS4_1=a
919          * => mlcc font ISO10646_UCS4_1 b
920          * => mlcc font ISO10646_UCS4_1 "" (results in ISO10646_UCS4_1=a)
921          */
922         destroy_table(targets[count]);
923         new_table(targets[count]);
924 
925         read_all_conf(targets[count], NULL);
926       }
927 
928       return 1;
929     } else {
930       ret = 1;
931       for (count = 0; count < num_targets; count++) {
932         read_all_conf(targets[count], file);
933       }
934     }
935   } else {
936     ret = 0;
937   }
938 
939   if (save && !save_conf(file, key, value)) {
940     return ret ? -1 : 0;
941   } else {
942     return ret;
943   }
944 }
945 
ui_get_config_font_name(ui_font_config_t * font_config,u_int font_size,vt_font_t font)946 char *ui_get_config_font_name(ui_font_config_t *font_config, u_int font_size, vt_font_t font) {
947   vt_font_t cand_font;
948   BL_PAIR(ui_font_name) pair;
949   char *font_name;
950   char *encoding;
951   size_t encoding_len;
952   int has_percentd;
953 #ifdef USE_XLIB
954   static char *orig_style[] = {"-medium-", "-r-", "-medium-r-"};
955   static char *new_style[] = {"-bold-", "-i-", "-bold-i-"};
956 #endif
957 
958   if (UNICODE_AREA(font)) {
959     font &= ~FONT_FULLWIDTH;
960   }
961 
962   encoding = NULL;
963   cand_font = NO_SIZE_ATTR(font);
964 
965   while (!(pair = get_font_name_pair(font_config->font_name_table, cand_font))) {
966 #ifdef USE_XLIB
967     int idx;
968 
969     if (font_config->type_engine == TYPE_XCORE) {
970       if ((idx = FONT_STYLE_INDEX(font)) >= 0 &&
971           ((pair = get_font_name_pair(font_config->font_name_table, FONT_CS(cand_font))) &&
972            (font_name = bl_str_replace(pair->value, orig_style[idx], new_style[idx])))) {
973 #ifdef DEBUG
974         bl_debug_printf(BL_DEBUG_TAG " Set font %s for %x\n", font_name, font);
975 #endif
976 
977         set_font_name_to_table(font_config->font_name_table, cand_font, font_name);
978 
979         continue;
980       }
981     } else
982 #endif
983     {
984       if (cand_font & FONT_STYLES) {
985         cand_font &= ~FONT_STYLES;
986 
987         continue;
988       }
989     }
990 
991     if (cand_font & FONT_FULLWIDTH) {
992       cand_font &= ~FONT_FULLWIDTH;
993     } else if (FONT_CS(cand_font) != DEFAULT_FONT) {
994       cand_font = DEFAULT_FONT;
995     } else {
996       return NULL;
997     }
998   }
999 
1000 #ifdef USE_XLIB
1001   if (font_config->type_engine == TYPE_XCORE && FONT_CS(cand_font) == DEFAULT_FONT &&
1002       /* encoding is appended if font_name is XLFD (not alias name). */
1003       (strchr(pair->value, '*') || strchr(pair->value, '-'))) {
1004     char **names;
1005 
1006     if ((names = ui_font_get_encoding_names(FONT_CS(font))) && names[0]) {
1007       encoding = names[0];
1008     }
1009   }
1010 #endif
1011 
1012 #if 1
1013   if (*(pair->value) == '&' &&
1014       /* XXX font variable is overwritten. */
1015       (font = parse_key(pair->value + 1)) != UNKNOWN_CS) {
1016     /*
1017      * XXX (Undocumented)
1018      *
1019      * JISX0213_2000_1 = &JISX0208_1983 in font configuration files.
1020      * => try to get a font name of JISX0208_1983 instead of
1021      *    JISX0213_2000_1 recursively.
1022      */
1023     return ui_get_config_font_name(font_config, font_size, font);
1024   }
1025 #endif
1026 
1027   /*
1028    * If pair->value is valid format or not is checked by is_valid_font_format()
1029    * in customize_font_name().
1030    * So all you have to do here is strchr( ... , '%') alone.
1031    */
1032   if (strchr(pair->value, '%')) {
1033     has_percentd = 1;
1034   } else if (encoding == NULL) {
1035     return strdup(pair->value);
1036   } else {
1037     has_percentd = 0;
1038   }
1039 
1040   if (!(font_name = malloc(strlen(pair->value) +
1041                            /* -2 is for "%d" */
1042                            (has_percentd ? DIGIT_STR_LEN(font_size) - 2 : 0) +
1043                            (encoding_len = encoding ? strlen(encoding) : 0) + 1))) {
1044     return NULL;
1045   }
1046 
1047   if (has_percentd) {
1048     sprintf(font_name, pair->value, font_size);
1049   } else {
1050     strcpy(font_name, pair->value);
1051   }
1052 
1053   if (encoding) {
1054     char *percent;
1055 
1056     if ((percent = strchr(font_name, ':'))) {
1057       /* -*-:200 -> -*-iso8859-1:200 */
1058 
1059       memmove(percent + encoding_len, percent, strlen(percent) + 1);
1060       memcpy(percent, encoding, encoding_len);
1061     } else {
1062       strcat(font_name, encoding);
1063     }
1064   }
1065 
1066   return font_name;
1067 }
1068 
ui_get_config_font_name2(const char * file,u_int font_size,char * font_cs)1069 char *ui_get_config_font_name2(const char *file, /* can be NULL */
1070                                u_int font_size, char *font_cs) {
1071   vt_font_t font;
1072   ui_font_config_t *font_config;
1073   ui_type_engine_t engine;
1074   ui_font_present_t present;
1075   char *font_name;
1076 
1077   if (file == NULL || strcmp(file, FONT_FILE) == 0) {
1078     engine = TYPE_XCORE;
1079     present = 0;
1080   }
1081 #if !defined(USE_FREETYPE) || !defined(USE_FONTCONFIG)
1082   else if (strcmp(file, aafont_file + 7) == 0) {
1083     engine = TYPE_XFT;
1084 
1085     /*
1086      * font_config::font_name_table is shared with
1087      * font_configs whose difference is only FONT_AA.
1088      */
1089     present = 0;
1090   } else if (strcmp(file, vaafont_file + 7) == 0) {
1091     engine = TYPE_XFT;
1092     present = FONT_VAR_WIDTH;
1093   } else if (strcmp(file, taafont_file + 7) == 0) {
1094     engine = TYPE_XFT;
1095     present = FONT_VERTICAL;
1096   }
1097 #endif
1098   else if (strcmp(file, VFONT_FILE) == 0) {
1099     engine = TYPE_XCORE;
1100     present = FONT_VAR_WIDTH;
1101   } else if (strcmp(file, TFONT_FILE) == 0) {
1102     engine = TYPE_XCORE;
1103     present = FONT_VERTICAL;
1104   } else {
1105 #ifdef DEBUG
1106     bl_debug_printf(BL_DEBUG_TAG " font file %s is not found.\n", file);
1107 #endif
1108 
1109     return NULL;
1110   }
1111 
1112   if ((font_config = ui_acquire_font_config(engine, present)) == NULL) {
1113 #ifdef DEBUG
1114     bl_debug_printf(BL_DEBUG_TAG " ui_font_config_t is not found.\n");
1115 #endif
1116 
1117     return NULL;
1118   }
1119 
1120   if ((font = parse_key(font_cs)) == UNKNOWN_CS) {
1121     return NULL;
1122   }
1123 
1124   font_name = ui_get_config_font_name(font_config, font_size, font);
1125 
1126   ui_release_font_config(font_config);
1127 
1128   return font_name;
1129 }
1130 
ui_get_config_font_names_all(ui_font_config_t * font_config,u_int font_size)1131 char *ui_get_config_font_names_all(ui_font_config_t *font_config, u_int font_size) {
1132   BL_PAIR(ui_font_name) * array;
1133   u_int size;
1134   char *font_name_list;
1135   size_t list_len;
1136   char *p;
1137   u_int count;
1138 
1139   array = get_font_name_pairs_array(&size, font_config->font_name_table);
1140 
1141   if (size == 0) {
1142     return NULL;
1143   }
1144 
1145   list_len = 0;
1146 
1147   for (count = 0; count < size; count++) {
1148     list_len += (strlen(array[count]->value) - 2 + DIGIT_STR_LEN(font_size) + 1);
1149   }
1150 
1151   if ((font_name_list = malloc(list_len)) == NULL) {
1152     return NULL;
1153   }
1154 
1155   p = font_name_list;
1156 
1157   for (count = 0; count < size; count++) {
1158     /*
1159      * XXX
1160      * Ignore DEFAULT_FONT setting because it doesn't have encoding name.
1161      */
1162     if (FONT_CS(array[count]->key) != DEFAULT_FONT) {
1163       sprintf(p, array[count]->value, font_size);
1164       p += strlen(p);
1165       *(p++) = ',';
1166     }
1167   }
1168 
1169   if (p > font_name_list) {
1170     --p;
1171   }
1172   *p = '\0';
1173 
1174 #ifdef DEBUG
1175   bl_debug_printf(BL_DEBUG_TAG " Font list is %s\n", font_name_list);
1176 #endif
1177 
1178   return font_name_list;
1179 }
1180 
ui_font_config_dump(ui_font_config_t * font_config)1181 char *ui_font_config_dump(ui_font_config_t *font_config) {
1182   BL_PAIR(ui_font_name) * array;
1183   u_int size;
1184   char *font_name_list;
1185   size_t list_len;
1186   char *p;
1187   u_int count;
1188 
1189   array = get_font_name_pairs_array(&size, font_config->font_name_table);
1190 
1191   if (size == 0) {
1192     return "No font settings";
1193   }
1194 
1195   list_len = 0;
1196   for (count = 0; count < size; count++) {
1197     list_len += (4 /* CSI2J */ + 15 /* ISO10646_UCS4_1, U+10FFFF-10FFFF */ +
1198                  12 /* _BOLD_ITALIC */ + 10 /* _FULLWIDTH */ + 1 /* = */ +
1199                  strlen(array[count]->value) + 2 /* \r\n or \0 */);
1200   }
1201 
1202   if ((font_name_list = malloc(list_len)) == NULL) {
1203     return "No font settings";
1204   }
1205 
1206   strcpy(font_name_list, "\x1b[2J");
1207   p = font_name_list + 4;
1208 
1209   for (count = 0; count < size; count++) {
1210     if (FONT_CS(array[count]->key) == DEFAULT_FONT) {
1211       strcpy(p, "DEFAULT");
1212       p += 7;
1213     } else {
1214       u_int count2;
1215 
1216       for (count2 = 0; count2 < sizeof(cs_table) / sizeof(cs_table[0]); count2++) {
1217         if (FONT_CS(array[count]->key) == cs_table[count2].cs) {
1218           int min;
1219           int max;
1220 
1221           if (vt_get_unicode_area(array[count]->key, &min, &max)) {
1222             sprintf(p, "U+%x-%x", min, max);
1223           } else {
1224             strcpy(p, cs_table[count2].name);
1225           }
1226           p += strlen(p);
1227 
1228           goto next_step;
1229         }
1230       }
1231 
1232       continue;
1233     }
1234 
1235   next_step:
1236     if (array[count]->key & FONT_BOLD) {
1237       strcpy(p, "_BOLD");
1238       p += 5;
1239     }
1240 
1241     if (array[count]->key & FONT_ITALIC) {
1242       strcpy(p, "_ITALIC");
1243       p += 7;
1244     }
1245 
1246     if (array[count]->key & FONT_FULLWIDTH) {
1247       strcpy(p, "_FULLWIDTH");
1248       p += 10;
1249     }
1250 
1251     sprintf(p, "=%s", array[count]->value);
1252     p += strlen(p);
1253 
1254     *(p++) = '\r';
1255     *(p++) = '\n';
1256   }
1257 
1258   *(p - 2) = '\0';
1259 
1260   return font_name_list;
1261 }
1262 
ui_get_charset_name(ef_charset_t cs)1263 char *ui_get_charset_name(ef_charset_t cs) {
1264   int count;
1265 
1266   for (count = 0; count < sizeof(cs_table) / sizeof(cs_table[0]); count++) {
1267     if (cs_table[count].cs == cs) {
1268       return cs_table[count].name;
1269     }
1270   }
1271 
1272   return NULL;
1273 }
1274 
1275 #ifdef BL_DEBUG
1276 
1277 #include <assert.h>
1278 
TEST_ui_font_config(void)1279 void TEST_ui_font_config(void) {
1280 #ifdef USE_XLIB
1281   ui_font_config_t *font_config;
1282   char *value;
1283 
1284   font_config = ui_font_config_new(TYPE_XCORE, 0);
1285   customize_font_name(font_config, ISO8859_1_R, "-hoge-medium-r-fuga-");
1286 
1287   value = ui_get_config_font_name(font_config, 12, ISO8859_1_R | FONT_BOLD);
1288   assert(strcmp("-hoge-bold-r-fuga-", value) == 0);
1289   free(value);
1290 
1291   value = ui_get_config_font_name(font_config, 12, ISO8859_1_R | FONT_ITALIC);
1292   assert(strcmp("-hoge-medium-i-fuga-", value) == 0);
1293   free(value);
1294 
1295   value = ui_get_config_font_name(font_config, 12, ISO8859_1_R | FONT_BOLD | FONT_ITALIC);
1296   assert(strcmp("-hoge-bold-i-fuga-", value) == 0);
1297   free(value);
1298 
1299   ui_font_config_destroy(font_config);
1300 
1301   bl_msg_printf("PASS ui_font_config test.\n");
1302 #endif
1303 }
1304 
1305 #endif
1306