Logo Search packages:      
Sourcecode: kbd version File versions  Download package

setfont.c

/*
 * setfont.c - Eugene Crosser & Andries Brouwer
 *
 * Version 1.05
 *
 * Loads the console font, and possibly the corresponding screen map(s).
 * We accept two kind of screen maps, one [-m] giving the correspondence
 * between some arbitrary 8-bit character set currently in use and the
 * font positions, and the second [-u] giving the correspondence between
 * font positions and Unicode values.
 */

#include <stdio.h>
#include <memory.h>
#include <fcntl.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <sys/ioctl.h>
#include <linux/kd.h>
#include <endian.h>
#include <sysexits.h>
#include "paths.h"
#include "getfd.h"
#include "findfile.h"
#include "loadunimap.h"
#include "psf.h"
#include "psffontop.h"
#include "kdfontop.h"
#include "kdmapop.h"
#include "xmalloc.h"
#include "nls.h"
#include "version.h"

static int position_codepage(int iunit);
static void saveoldfont(int fd, char *ofil);
static void saveoldfontplusunicodemap(int fd, char *Ofil);
static void loadnewfont(int fd, char *ifil,
                  int iunit, int hwunit, int no_m, int no_u);
static void loadnewfonts(int fd, char **ifiles, int ifilct,
                  int iunit, int hwunit, int no_m, int no_u);
extern void saveoldmap(int fd, char *omfil);
extern void loadnewmap(int fd, char *mfil);
extern void activatemap(int fd);
extern void disactivatemap(int fd);

int verbose = 0;
int force = 0;

/* search for the font in these directories (with trailing /) */
char *fontdirpath[] = { "", DATADIR "/" FONTDIR "/", 0 };
char *fontsuffixes[] = { "", ".psfu", ".psf", ".cp", ".fnt", 0 };
/* hide partial fonts a bit - loading a single one is a bad idea */
char *partfontdirpath[] = { "", DATADIR "/" FONTDIR "/" PARTIALDIR "/", 0 };
char *partfontsuffixes[] = { "", 0 };

static inline FILE*
findfont(char *fnam) {
    return findfile(fnam, fontdirpath, fontsuffixes);
}

static inline FILE*
findpartialfont(char *fnam) {
    return findfile(fnam, partfontdirpath, partfontsuffixes);
}

static void
usage(void)
{
        fprintf(stderr, _(
"Usage: setfont [write-options] [-<N>] [newfont..] [-m consolemap] [-u unicodemap]\n"
"  write-options (take place before file loading):\n"
"    -o  <filename>  Write current font to <filename>\n"
"    -O  <filename>  Write current font and unicode map to <filename>\n"
"    -om <filename>  Write current consolemap to <filename>\n"
"    -ou <filename>  Write current unicodemap to <filename>\n"
"If no newfont and no -[o|O|om|ou|m|u] option is given,\n"
"a default font is loaded:\n"
"    setfont         Load font \"default[.gz]\"\n"
"    setfont -<N>    Load font \"default8x<N>[.gz]\"\n"
"The -<N> option selects a font from a codepage that contains three fonts:\n"
"    setfont -{8|14|16} codepage.cp[.gz]   Load 8x<N> font from codepage.cp\n"
"Explicitly (with -m or -u) or implicitly (in the fontfile) given mappings\n"
"will be loaded and, in the case of consolemaps, activated.\n"
"    -h<N>      (no space) Override font height.\n"
"    -m <fn>    Load console screen map.\n"
"    -u <fn>    Load font unicode map.\n"
"    -m none    Suppress loading and activation of a screen map.\n"
"    -u none    Suppress loading of a unicode map.\n"
"    -v         Be verbose.\n"
"    -C <cons>  Indicate console device to be used.\n"
"    -V         Print version and exit.\n"
"Files are loaded from the current directory or /usr/lib/kbd/*/.\n"
));
      exit(EX_USAGE);
}

#define MAXIFILES 256

int
main(int argc, char *argv[]) {
      char *ifiles[MAXIFILES];
      char *mfil, *ufil, *Ofil, *ofil, *omfil, *oufil, *console;
      int ifilct = 0, fd, i, iunit, hwunit, no_m, no_u;
      int restore = 0;

      set_progname(argv[0]);

      setlocale(LC_ALL, "");
      bindtextdomain(PACKAGE, LOCALEDIR);
      textdomain(PACKAGE);

      ifiles[0] = mfil = ufil = Ofil = ofil = omfil = oufil = NULL;
      iunit = hwunit = 0;
      no_m = no_u = 0;
      console = NULL;

      /*
       * No getopt() here because of the -om etc options.
       */
      for (i = 1; i < argc; i++) {
          if (!strcmp(argv[i], "-V")) {
            print_version_and_exit();
          } else if (!strcmp(argv[i], "-v")) {
              verbose++;
          } else if (!strcmp(argv[i], "-R")) {
            restore = 1;
          } else if (!strcmp(argv[i], "-C")) {
            if (++i == argc || console)
              usage();
            console = argv[i];
          } else if (!strcmp(argv[i], "-O")) {
            if (++i == argc || Ofil)
              usage();
            Ofil = argv[i];
          } else if (!strcmp(argv[i], "-o")) {
            if (++i == argc || ofil)
              usage();
            ofil = argv[i];
          } else if (!strcmp(argv[i], "-om")) {
            if (++i == argc || omfil)
              usage();
            omfil = argv[i];
          } else if (!strcmp(argv[i], "-ou")) {
            if (++i == argc || oufil)
              usage();
            oufil = argv[i];
          } else if (!strcmp(argv[i], "-m")) {
            if (++i == argc || mfil)
              usage();
            if (!strcmp(argv[i], "none"))
              no_m = 1;
            else
              mfil = argv[i];
          } else if (!strcmp(argv[i], "-u")) {
            if (++i == argc || ufil)
              usage();
            if (!strcmp(argv[i], "none"))
              no_u = 1;
            else
              ufil = argv[i];
          } else if (!strcmp(argv[i], "-f")) {
            force = 1;
          } else if (!strncmp(argv[i], "-h", 2)) {
            hwunit = atoi(argv[i]+2);
            if (hwunit <= 0 || hwunit > 32)
              usage();
          } else if (argv[i][0] == '-') {
            iunit = atoi(argv[i]+1);
            if(iunit <= 0 || iunit > 32)
              usage();
          } else {
            if (ifilct == MAXIFILES) {
                fprintf(stderr, _("setfont: too many input files\n"));
                exit(EX_USAGE);
            }
            ifiles[ifilct++] = argv[i];
          }
      }

      if (ifilct && restore) {
          fprintf(stderr, _("setfont: cannot both restore from character ROM"
                        " and from file. Font unchanged.\n"));
          exit(EX_USAGE);
      }

      fd = getfd(console);

      if (!ifilct && !mfil && !ufil &&
          !Ofil && !ofil && !omfil && !oufil && !restore)
        /* reset to some default */
        ifiles[ifilct++] = "";

      if (Ofil)
        saveoldfontplusunicodemap(fd, Ofil);

      if (ofil)
        saveoldfont(fd, ofil);

      if (omfil)
        saveoldmap(fd, omfil);

      if (oufil)
        saveunicodemap(fd, oufil);

      if (mfil) {
          loadnewmap(fd, mfil);
          activatemap(fd);
          no_m = 1;
      }

      if (ufil)
        no_u = 1;

      if (restore)
        restorefont(fd);

      if (ifilct)
        loadnewfonts(fd, ifiles, ifilct, iunit, hwunit, no_m, no_u);

      if (ufil)
        loadunicodemap(fd, ufil);

      return 0;
}

/*
 * 0 - do not test, 1 - test and warn, 2 - test and wipe, 3 - refuse
 */
static int erase_mode = 1;

static void
do_loadfont(int fd, char *inbuf, int width, int height, int hwunit,
          int fontsize, char *pathname) {
      char *buf;
      int i, buflen;
      int bytewidth = (width+7)/8;
      int charsize = height * bytewidth;
      int kcharsize = 32 * bytewidth;
      int bad_video_erase_char = 0;

      if (height < 1 || height > 32) {
            fprintf(stderr, _("Bad character height %d\n"), height);
            exit(EX_DATAERR);
      }
      if (width < 1 || width > 32) {
            fprintf(stderr, _("Bad character width %d\n"), width);
            exit(EX_DATAERR);
      }

      if (!hwunit)
            hwunit = height;

      buflen = kcharsize * ((fontsize < 128) ? 128 : fontsize);
      buf = xmalloc(buflen);
      memset(buf,0,buflen);

      for (i = 0; i < fontsize; i++)
            memcpy(buf+(i*kcharsize), inbuf+(i*charsize), charsize);

      /*
       * Due to a kernel bug, font position 32 is used
       * to erase the screen, regardless of maps loaded.
       * So, usually this font position should be blank.
       */
      if (erase_mode) {
            for (i = 0; i < kcharsize; i++)
                  if (buf[32*kcharsize+i])
                        bad_video_erase_char = 1;
            if (bad_video_erase_char) {
                  fprintf(stderr,
                        _("%s: font position 32 is nonblank\n"),
                        progname);
                  switch(erase_mode) {
                  case 3:
                        exit(EX_DATAERR);
                  case 2:
                        for (i = 0; i < kcharsize; i++)
                              buf[32*kcharsize+i] = 0;
                        fprintf(stderr, _("%s: wiped it\n"), progname);
                        break;
                  case 1:
                        fprintf(stderr,
                              _("%s: background will look funny\n"),
                              progname);
                  }
                  fflush(stderr);
                  sleep(2);
            }
      }

      if (verbose) {
            if (height == hwunit && pathname)
                  printf(_("Loading %d-char %dx%d font from file %s\n"),
                         fontsize, width, height, pathname);
            else if (height == hwunit)
                  printf(_("Loading %d-char %dx%d font\n"),
                         fontsize, width, height);
            else if (pathname)
                  printf(_("Loading %d-char %dx%d (%d) font from file %s\n"),
                         fontsize, width, height, hwunit, pathname);
            else
                  printf(_("Loading %d-char %dx%d (%d) font\n"),
                         fontsize, width, height, hwunit);
      }

      if (putfont(fd, buf, fontsize, width, hwunit))
            exit(EX_OSERR);
}

static void
do_loadtable(int fd, struct unicode_list *uclistheads, int fontsize) {
      struct unimapdesc ud;
      struct unipair *up;
      int i, ct = 0, maxct;
      struct unicode_list *ul;
      struct unicode_seq *us;

      maxct = 0;
      for (i = 0; i < fontsize; i++) {
            ul = uclistheads[i].next;
            while(ul) {
                  us = ul->seq;
                  if (us && ! us->next)
                        maxct++;
                  ul = ul->next;
            }
      }
      up = xmalloc(maxct * sizeof(struct unipair));
      for (i = 0; i < fontsize; i++) {
            ul = uclistheads[i].next;
            while(ul) {
                  us = ul->seq;
                  if (us && ! us->next) {
                        up[ct].unicode = us->uc;
                        up[ct].fontpos = i;
                        ct++;
                  }
                  ul = ul->next;
            }
      }
      if (ct != maxct) {
            char *u = _("%s: bug in do_loadtable\n");
            fprintf(stderr, u, progname);
            exit(EX_SOFTWARE);
      }

      if (verbose)
        printf(_("Loading Unicode mapping table...\n"));

      ud.entry_ct = ct;
      ud.entries = up;
      if (loadunimap(fd, NULL, &ud))
            exit(EX_OSERR);
}

static void
loadnewfonts(int fd, char **ifiles, int ifilct,
           int iunit, int hwunit, int no_m, int no_u) {
      FILE *fpi;
      char *ifil, *inbuf, *fontbuf, *bigfontbuf;
      int inputlth, fontbuflth, fontsize, height, width, bytewidth;
      int bigfontbuflth, bigfontsize, bigheight, bigwidth;
      struct unicode_list *uclistheads;
      int i;

      if (ifilct == 1) {
            loadnewfont(fd, ifiles[0], iunit, hwunit, no_m, no_u);
            return;
      }

      /* several fonts that must be merged */
      /* We just concatenate the bitmaps - only allow psf fonts */
      bigfontbuf = NULL;
      bigfontbuflth = 0;
      bigfontsize = 0;
      uclistheads = NULL;
      bigheight = 0;
      bigwidth = 0;

      for (i=0; i<ifilct; i++) {
            ifil = ifiles[i];
            if ((fpi = findfont(ifil)) == NULL &&
                (fpi = findpartialfont(ifil)) == NULL) {
                  fprintf(stderr, _("Cannot open font file %s\n"), ifil);
                  exit(EX_NOINPUT);
            }

            inbuf = fontbuf = NULL;
            inputlth = fontbuflth = 0;
            fontsize = 0;

            if(readpsffont(fpi, &inbuf, &inputlth, &fontbuf, &fontbuflth,
                         &width, &fontsize, bigfontsize,
                         no_u ? NULL : &uclistheads)) {
                  fprintf(stderr, _("When loading several fonts, all "
                                "must be psf fonts - %s isn't\n"),
                        pathname);
                  exit(EX_DATAERR);
            }
            bytewidth = (width+7) / 8;
            height = fontbuflth / (bytewidth * fontsize);
            if (verbose)
                  printf(_("Read %d-char %dx%d font from file %s\n"),
                         fontsize, width, height, pathname);

            if (bigheight == 0)
                  bigheight = height;
            else if (bigheight != height) {
                  fprintf(stderr, _("When loading several fonts, all "
                                "must have the same height\n"));
                  exit(EX_DATAERR);
            }
            if (bigwidth == 0)
                  bigwidth = width;
            else if (bigwidth != width) {
                  fprintf(stderr, _("When loading several fonts, all "
                                "must have the same width\n"));
                  exit(EX_DATAERR);
            }

            bigfontsize += fontsize;
            bigfontbuflth += fontbuflth;
            bigfontbuf = xrealloc(bigfontbuf, bigfontbuflth);
            memcpy(bigfontbuf+bigfontbuflth-fontbuflth,
                   fontbuf, fontbuflth);
      }
      do_loadfont(fd, bigfontbuf, bigwidth, bigheight, hwunit,
                bigfontsize, NULL);

      if (uclistheads && !no_u)
            do_loadtable(fd, uclistheads, bigfontsize);
}

static void
loadnewfont(int fd, char *ifil, int iunit, int hwunit, int no_m, int no_u) {
      FILE *fpi;
      char defname[20];
      int height, width, bytewidth, def = 0;
      char *inbuf, *fontbuf;
      int inputlth, fontbuflth, fontsize, offset;
      struct unicode_list *uclistheads;

      if (!*ifil) {
            /* try to find some default file */

            def = 1;          /* maybe also load default unimap */

            if (iunit < 0 || iunit > 32)
                  iunit = 0;
            if (iunit == 0) {
                  if ((fpi = findfont(ifil = "default")) == NULL &&
                      (fpi = findfont(ifil = "default8x16")) == NULL &&
                      (fpi = findfont(ifil = "default8x14")) == NULL &&
                      (fpi = findfont(ifil = "default8x8")) == NULL) {
                        fprintf(stderr, _("Cannot find default font\n"));
                        exit(EX_NOINPUT);
                  }
            } else {
                  sprintf(defname, "default8x%d", iunit);
                  if ((fpi = findfont(ifil = defname)) == NULL &&
                      (fpi = findfont(ifil = "default")) == NULL) {
                        fprintf(stderr, _("Cannot find %s font\n"), ifil);
                        exit(EX_NOINPUT);
                  }
            }
      } else {
            if ((fpi = findfont(ifil)) == NULL) {
                  fprintf(stderr, _("Cannot open font file %s\n"), ifil);
                  exit(EX_NOINPUT);
            }
      }

      if (verbose > 1)
            printf(_("Reading font file %s\n"), ifil);

      inbuf = fontbuf = NULL;
      inputlth = fontbuflth = fontsize = 0;
      width = 8;
      uclistheads = NULL;
      if(readpsffont(fpi, &inbuf, &inputlth, &fontbuf, &fontbuflth,
                   &width, &fontsize, 0,
                   no_u ? NULL : &uclistheads) == 0) {
            /* we've got a psf font */
            bytewidth = (width+7) / 8;
            height = fontbuflth / (bytewidth * fontsize);

            do_loadfont(fd, fontbuf, width, height, hwunit,
                      fontsize, pathname);
            if (uclistheads && !no_u)
                  do_loadtable(fd, uclistheads, fontsize);
#if 1
            if (!uclistheads && !no_u && def)
                  loadunicodemap(fd, "def.uni");
#endif
            return;
      }

      /* instructions to combine fonts? */
      { char *combineheader = "# combine partial fonts\n";
        int chlth = strlen(combineheader);
        char *p, *q;
        if (inputlth >= chlth && !strncmp(inbuf, combineheader, chlth)) {
              char *ifiles[MAXIFILES];
              int ifilct = 0;
              q = inbuf + chlth;
              while(q < inbuf + inputlth) {
                    p = q;
                    while (q < inbuf+inputlth && *q != '\n')
                          q++;
                    if (q == inbuf+inputlth) {
                          fprintf(stderr,
                                _("No final newline in combine file\n"));
                          exit(EX_DATAERR);
                    }
                    *q++ = 0;
                    if (ifilct == MAXIFILES) {
                          fprintf(stderr,
                                _("Too many files to combine\n"));
                          exit(EX_DATAERR);
                    }
                    ifiles[ifilct++] = p;
              }
              /* recursive call */
              loadnewfonts(fd, ifiles, ifilct, iunit, hwunit, no_m, no_u);
              return;
        }
      }

      /* file with three code pages? */
      if (inputlth == 9780) {
            offset = position_codepage(iunit);
            height = iunit;
            fontsize = 256;
            width = 8;
      } else if (inputlth == 32768) {
            /* restorefont -w writes a SVGA font to file
               restorefont -r restores it
               These fonts have size 32768, for two 512-char fonts.
               In fact, when BROKEN_GRAPHICS_PROGRAMS is defined,
               and it always is, there is no default font that is saved,
               so probably the second half is always garbage. */
            fprintf(stderr, _("Hmm - a font from restorefont? "
                          "Using the first half.\n"));
            inputlth = 16384;       /* ignore rest */
            fontsize = 512;
            offset = 0;
            width = 8;
            height = 32;
            if (!hwunit)
                  hwunit = 16;
      } else {
            int rem = (inputlth % 256);
            if (rem == 0 || rem == 40) {
                  /* 0: bare code page bitmap */
                  /* 40: preceded by .cp header */
                  /* we might check some header details */
                  offset = rem;
            } else {
                  fprintf(stderr, _("Bad input file size\n"));
                  exit(EX_DATAERR);
            }
            fontsize = 256;
            width = 8;
            height = inputlth/256;
      }
      do_loadfont(fd, inbuf+offset, width, height, hwunit, fontsize,
                pathname);
}

static int
position_codepage(int iunit) {
        int offset;

      /* code page: first 40 bytes, then 8x16 font,
         then 6 bytes, then 8x14 font,
         then 6 bytes, then 8x8 font */

      if (!iunit) {
          fprintf(stderr,
                _("This file contains 3 fonts: 8x8, 8x14 and 8x16."
                  " Please indicate\n"
                  "using an option -8 or -14 or -16 "
                  "which one you want loaded.\n"));
          exit(EX_USAGE);
      }
      switch (iunit) {
        case 8:
          offset = 7732; break;
        case 14:
          offset = 4142; break;
        case 16:
          offset = 40; break;
        default:
          fprintf(stderr, _("You asked for font size %d, "
                        "but only 8, 14, 16 are possible here.\n"),
                iunit);
          exit(EX_USAGE);
      }
      return offset;
}

static void
do_saveoldfont(int fd, char *ofil, FILE *fpo, int unimap_follows,
             int *count, int *utf8) {

/* this is the max font size the kernel is willing to handle */
#define MAXFONTSIZE     65536
      char buf[MAXFONTSIZE];

      int i, ct, width, height, bytewidth, charsize, kcharsize;

      ct = sizeof(buf)/(32*32/8);   /* max size 32x32, 8 bits/byte */
      if (getfont(fd, buf, &ct, &width, &height))
            exit(EX_OSERR);

      /* save as efficiently as possible */
      bytewidth = (width + 7) / 8;
      height = font_charheight(buf, ct, width);
      charsize = height * bytewidth;
      kcharsize = 32 * bytewidth;

      /* Do we need a psf header? */
      /* Yes if ct==512 - otherwise we cannot distinguish
         a 512-char 8x8 and a 256-char 8x16 font. */
#define ALWAYS_PSF_HEADER     1

      if (ct != 256 || width != 8 || unimap_follows || ALWAYS_PSF_HEADER) {
            int psftype = 1;
            int flags = 0;

            if (unimap_follows)
                  flags |= WPSFH_HASTAB;
            writepsffontheader (fpo, width, height, ct, &psftype, flags);
            if (utf8)
                  *utf8 = (psftype == 2);
      }

      if (height == 0) {
            fprintf(stderr, _("Found nothing to save\n"));
      } else {
            for (i = 0; i < ct; i++) {
                  if (fwrite(buf+(i*kcharsize), charsize, 1, fpo) != 1) {
                        fprintf(stderr, _("Cannot write font file"));
                        exit(EX_IOERR);
                  }
            }
            if (verbose) {
                  printf(_("Saved %d-char %dx%d font file on %s\n"),
                         ct, width, height, ofil);
            }
      }

      if (count)
            *count = ct;
}

static void
saveoldfont(int fd, char *ofil) {
    FILE *fpo;

    if((fpo = fopen(ofil, "w")) == NULL) {
      perror(ofil);
      exit(EX_CANTCREAT);
    }
    do_saveoldfont(fd, ofil, fpo, 0, NULL, NULL);
    fclose(fpo);
}

static void
saveoldfontplusunicodemap(int fd, char *Ofil) {
    FILE *fpo;
    int ct;
    int utf8 = 0;

    if((fpo = fopen(Ofil, "w")) == NULL) {
      perror(Ofil);
      exit(EX_CANTCREAT);
    }
    ct = 0;
    do_saveoldfont(fd, Ofil, fpo, 1, &ct, &utf8);
    appendunicodemap(fd, fpo, ct, utf8);
    fclose(fpo);
}

/* Only on the current console? On all allocated consoles? */
/* A newly allocated console has NORM_MAP by default -
   probably it should copy the default from the current console?
   But what if we want a new one because the current one is messed up? */
/* For the moment: only the current console, only the G0 set */

static void
send_escseq(int fd, char *seq, int n) {
      if (write(fd, seq, n) != n)  /* maybe fd is read-only */
            printf(seq);
}

void
activatemap(int fd) {
      send_escseq(fd, "\033(K", 3);
}

void
disactivatemap(int fd) {
      send_escseq(fd, "\033(B", 3);
}

Generated by  Doxygen 1.6.0   Back to index