Back to index

tetex-bin  3.0
psearch.c
Go to the documentation of this file.
00001 /*
00002  * FILE:    psearch.c
00003  * PURPOSE: PATH search module
00004  * VERSION: 1.0 (Nov. 1995)
00005  * AUTHOR:  Piet Tutelaers (rcpt@urc.tue.nl)
00006  */
00007 
00008 #define _POSIX_SOURCE 1
00009 #ifdef ultrix
00010 #define __POSIX             /* otherwise MAXNAMLEN undefined */
00011 #endif
00012 
00013 #include <sys/types.h>
00014 #include <sys/stat.h>   /* struct stat */
00015 #include <dirent.h>  /* opendir(), readdir(), closedir() */
00016 #include "ctype.h"   /* isalpha() */
00017 #include <stdio.h>   /* sprintf(), fclose() */
00018 #include "stdlib.h"  /* getenv(), malloc() */
00019 #include <stdarg.h>  /* va_start(), va_arg(), va_end() */
00020 #include "string.h"  /* strcat(), strchr(), strcmp(), strcpy(), strlen(),
00021                         strncmp(), strncpy(), strstr() */
00022 #include "basics.h"  /* fatal() */
00023 #include "strexpr.h" /* strexpr() */
00024 #include "filenames.h"      /* equal(), extension() */
00025 #include "texfiles.h"       /* four(), one(), two() */
00026 
00027 #ifdef UNIX
00028 #include <pwd.h>     /* getpwnam() */
00029 
00030 int  tilde = 1;             /* Substitute ~ and ~user */
00031 static char *home = NULL;
00032 
00033 /*
00034  * During scanning of paths we often need to replace ~user with user's
00035  * HOME directory and ~ with HOME directory. Subtilde() copies the actual
00036  * value of `~' or `~user' from <path> into <fn>. The pointers <fn> and
00037  * <path> are `updated'.
00038  */
00039 void subtilde(char **fn, char **path)
00040 {  char pattern[32], *p; int i;
00041 
00042    i = 0; p = *path;
00043    while (*p != '\0' && *p != DIRSEP && *p != PATHSEP)
00044       pattern[i++]  = *p++ ;
00045    pattern[i] = '\0' ;
00046    if (pattern[0] != '~') return;
00047    if (pattern[1] == '\0') {
00048       if (home == NULL) 
00049          home = getenv("HOME");
00050       if (home == NULL) fatal("You don't have a HOME!\n");
00051       sprintf(*fn, "%s\0", home);
00052       *fn += strlen(*fn);
00053       *path = p;
00054    }
00055    else {
00056       struct passwd *pw;
00057       if (pw = getpwnam(&pattern[1])) {
00058          sprintf(*fn, "%s\0", pw->pw_dir);
00059          *fn += strlen(*fn); *path = p;
00060       }
00061       else
00062          fatal("%s: no such user\n", &pattern[1]) ;
00063    }
00064 }
00065 #else
00066 static int  tilde = 0;    /* Don't substitute ~ and ~user */
00067 static void subtilde(char **fn, char **path) {}
00068 #endif
00069 
00070 /*
00071  * Return a path constructed by <env> and <deFault>. If <env> is empty
00072  * the returned path equals <deFault>, otherwise the returned path is
00073  * <env> in which empty path elements are replaced by <deFault>. Double
00074  * path elements are NOT removed.
00075  */
00076 char *path(char *deFault, char *env) {
00077    char *e, *p, *q; int len, left = MAXPATHLEN;
00078 
00079    if (env == NULL) return deFault;
00080 
00081    p = (char *) malloc(MAXPATHLEN);
00082    if (p == NULL) fatal("Out of memory\n");
00083 
00084    q = p; e = env;
00085    while (*e) {
00086       if (*e == PATHSEP) {
00087          if (left == MAXPATHLEN) {
00088            len = strlen(deFault);
00089             if (len > left-1) fatal("'%s' path too long!\n", env);
00090            strcpy(q, deFault);
00091            q += len; left -= len;
00092            *q++ = *e++; left--;
00093         }
00094         else if (*(e+1) == PATHSEP || *(e+1) == '\0') {
00095            *q++ = *e++; left--;
00096             len = strlen(deFault);
00097            if (len > left) fatal("'%s' path too long!\n", env);     
00098             strcpy(q, deFault);
00099            q += len; left -= len;
00100         }
00101         else {
00102           *q++ = *e++; left--;
00103         }
00104       }
00105       else {
00106          *q++ = *e++; left--;
00107       }
00108       if (left == 0) fatal("'%s' path too long!\n", env);
00109    }
00110    *q = '\0';
00111    return p;
00112 }
00113 
00114 /*
00115  * Rsearch() searches <file> through <path>. Recursion introduced by
00116  * RECURSIVE string (system dependant) will be followed. Search stops
00117  * after first match.
00118  */
00119 static char *rsearch(char *path, char *file)
00120 {
00121    struct stat status;
00122    DIR *D;
00123    struct dirent *de;
00124    int i;
00125    char *res = NULL, *ext, *file_ext,
00126         head[MAXPATHLEN], target[MAXPATHLEN], *tail;
00127    char fn[MAXPATHLEN];
00128 
00129    /* determine head and tail of path */
00130    i = 0;
00131    while (*(path+i) != '\0' && strncmp(path+i, RECURSIVE, 2) != 0) {
00132       head[i] = *(path+i); i++;
00133    }
00134    head[i] = '\0';
00135    if (*(path+i) == '\0') tail = NULL;
00136    else tail = path+i+2;
00137 
00138    /* Check if we have found <file> */
00139    if (tail == NULL || *tail == '\0') {
00140       sprintf(fn, "%s%c%s", head, DIRSEP, file);
00141       if (stat(fn, &status) == 0) {
00142         res = malloc(strlen(fn)+1);
00143         if (res == NULL) fatal("Out of memory\n");
00144         strcpy(res, fn);
00145         return res;
00146       }
00147       /* No recursion, so stop */
00148       if (tail == NULL) return NULL;
00149    }
00150 
00151    if ((D = opendir(head)) == NULL) return NULL;
00152    file_ext = extension(file);
00153 
00154    /* now read directories and probe files */
00155    while ((de = readdir(D)) != NULL) {
00156       /* Ignore `.' and `..' directories */
00157       if (strcmp(de->d_name, ".") == 0) continue;
00158       if (strcmp(de->d_name, "..") == 0) continue;
00159 
00160       /* Check if we have a directory */
00161       sprintf(fn, "%s%c%s", head, DIRSEP, de->d_name);
00162       (void) stat(fn, &status);
00163       if (!S_ISDIR(status.st_mode)) {
00164          /* if we are looking for a file with an extension and we find
00165           * files with the same or a different extension we may consider
00166           * this directory as a wrong place unless the user has defined
00167           * funny directory search paths
00168           */
00169          if (file_ext && (ext = extension(de->d_name))) {
00170             if (equal(ext, ".pk")) break;
00171             if (equal(ext, ".vf")) break;
00172             if (equal(ext, ".mf")) break;
00173             if (equal(ext, ".tfm")) break;
00174             if (equal(ext, ".afm")) break;
00175          }
00176          continue;
00177       }
00178 
00179       /* Directories with no real subdirectories can be skipped
00180        * unless the file we are looking for is there. Unfortunately
00181        * this shortcut only works for UNIX systems. On other systems
00182        * the above check on extensions may help.
00183        */
00184 #if defined(UNIX)
00185       if (status.st_nlink == 2) {
00186         if (strchr(file, DIRSEP)) continue;
00187         if ((*tail == '\0') && (res = rsearch(fn, file))) break;
00188         continue;
00189       }
00190 #endif
00191 
00192       /* path of the form "/aap/noot/mies//" */
00193       if (*tail == '\0') {
00194          sprintf(fn, "%s%c%s%s", head, DIRSEP, de->d_name, RECURSIVE);
00195         if (res = rsearch(fn, file)) break;
00196         continue;
00197       }
00198       
00199       /* path of the form "/aap/noot//mies" */
00200 
00201       /* search /aap/noot/<d_name>// <tail>/<file> */
00202       sprintf(fn, "%s%c%s%s", head, DIRSEP, de->d_name, RECURSIVE);
00203       sprintf(target, "%s%c%s", tail, DIRSEP, file);
00204       if (res = rsearch(fn, target)) break;
00205    }
00206    closedir(D);
00207    return res;
00208 }
00209 
00210 /* Is <name> an absolute filename? */
00211 int absname(char *name) {
00212 
00213    if (*name == DIRSEP) return 1;
00214 #ifdef MSDOS
00215    if (*(name+1) == ':' && *(name+2) == DIRSEP && isalpha(*name)) return 1;
00216 #endif
00217    return 0;
00218 }
00219 
00220 /*
00221  * Look for a <file> in <path>. Return absolute filename if found.
00222  * If not found return NULL (if <terminate> is 0) or terminate program
00223  * with appropriate message (if <terminate> is not equal to 0).
00224  */
00225 char *search_file(char *path, char *file, int terminate) {
00226    char *fn, pe[MAXPATHLEN], *ppe, *ppath;
00227    struct stat status;
00228 
00229    if (path == NULL || *path == '\0' || absname(file)) {
00230       if (stat(file, &status) == 0) return file;
00231       if (terminate) fatal("File <%s> not found\n", file);
00232       return NULL;
00233    }
00234    ppath = path;
00235    while (*ppath) {
00236       /* copy next pathelement into pe */
00237       ppe = pe;
00238       while (*ppath != PATHSEP && *ppath != '\0') {
00239          if (tilde && *ppath == '~')
00240             subtilde(&ppe, &ppath);
00241          else
00242             *ppe++ = *ppath++;
00243       }
00244       *ppe = '\0';
00245       if (*ppath == PATHSEP) ppath++;
00246       if (fn = rsearch(pe, file)) return fn;
00247    }
00248    if (terminate) fatal("File <%s> not found in path \"%s\"\n", file, path);
00249    return NULL;
00250 }
00251 
00252 /*
00253  * Now some functions for PK font search
00254  */
00255 
00256 /*
00257  * replace <pattern> in <cs> by <value>. If the <pattern> is %b or %d
00258  * the value will be converted to a decimal value.
00259  */
00260 static void replace(char *cs, char *pattern, void *value) {
00261    char temp[MAXPATHLEN], *p, *q; int len;
00262 
00263    while (q = strstr(cs, pattern)) {
00264       strcpy(temp, q+strlen(pattern));
00265       switch (*(pattern+1)) {
00266       case 'b':
00267       case 'd':
00268          sprintf(q, "%d", *((int *)value));
00269          len = strlen(q);
00270          break;
00271       case 'F':
00272          sprintf(q, "%-8s", (char *) value);
00273          len = strlen (q);
00274          break;
00275       case 'f':
00276          sprintf(q, "%s", (char *) value);
00277          len = strlen (q);
00278          break;
00279       default:
00280          sprintf(q, "%s", (char *) value);
00281          len = strlen (q);
00282          break;
00283       }
00284       strcpy(q+len, temp);
00285    }
00286 }
00287 
00288 /*
00289  * This function substitutes the <patterns> encountered in <str>
00290  * with their corresponding <value>. Recognized patterns are %b, %d,
00291  * %f, %F and %m. They will be replaced with their corresponding integer
00292  * (%b, %d) or string values (%f, %F and %m). These values can be used as
00293  * placeholders for basic resolution (%b), resolution (%d), font name
00294  * (%f or %F) or mode definition (%m). The %F is a special case for
00295  * representing the fontname leftadjusted in a string of eight characters.
00296  */
00297 
00298 void substitute(char *str, char *patterns, ...) {
00299    va_list ap;
00300    char *p;
00301    char *svalue; int ivalue;
00302 
00303    /* get arguments ... */
00304    va_start(ap, patterns);
00305    for (p = patterns; *p; p++) {
00306       if (*p != '%') {
00307         fatal("%s: invalid format\n");
00308       }
00309       switch (*++p) {
00310       case 'b':
00311          ivalue = va_arg(ap, int);
00312         replace(str, "%b", &ivalue);
00313         break;
00314       case 'd':
00315          ivalue = va_arg(ap, int);
00316         replace(str, "%d", &ivalue);
00317         break;
00318       case 'F':
00319          svalue = va_arg(ap, char *);
00320         replace(str, "%F", svalue);
00321         break;
00322       case 'f':
00323          svalue = va_arg(ap, char *);
00324         replace(str, "%f", svalue);
00325         break;
00326       case 'm':
00327          svalue = va_arg(ap, char *);
00328         replace(str, "%m", svalue);
00329         break;
00330       case 'p':
00331          svalue = va_arg(ap, char *);
00332         replace(str, "%p", svalue);
00333         break;
00334       default:
00335         fatal("%s: invalid letter (%c) in format\n", patterns, *p);
00336       }
00337    }
00338    va_end(ap);
00339 }
00340 
00341 /* global font searching parameters */
00342 static char *_mode = NULL;
00343 static int _margin = 0, _bdpi = -1;
00344 static char *_tolerance = NULL;
00345 
00346 void init_pksearch(int bdpi, char *mode, int margin, char *tolerance) {
00347    _bdpi = bdpi;
00348    _mode = mode;
00349    _margin = margin;
00350    _tolerance = tolerance;
00351 }
00352 
00353 /* evaluate <expr> after substituting %d with <dpi>. Return the 
00354  * integer result or abort with message when invalid expression.
00355  */
00356 int evaluate(char *expr, int dpi) {
00357    char expression[128];
00358    int result;
00359 
00360    if (expr == NULL || *expr == '\0') return 0;
00361    strcpy(expression, expr);
00362    substitute(expression, "%d", dpi);
00363    if (strexpr(&result, expression))
00364       fatal("%s: expression error\n", expression);
00365    return result;
00366 }
00367 
00368 #define INT(x) ((int)(x+0.5))
00369 
00370 /*
00371  * Round <dpi> to a whole or half magstep if the difference is within
00372  * a certain _tolerance from it.
00373  */
00374 int nearesthalf(int dpi) {
00375    double near = _bdpi, half = 1.095445115; int tolerance;
00376 
00377    tolerance = evaluate(_tolerance, dpi);
00378    if (tolerance == 0 || dpi == _bdpi) return dpi;
00379    if (dpi > INT(near)+tolerance)
00380       while (dpi > INT(near)+tolerance) near*=half;
00381    else if (dpi < INT(near)-tolerance)
00382       while (dpi < INT(near)-tolerance) near/=half;
00383    if (dpi >= INT(near)-tolerance && dpi <= INT(near)+tolerance)
00384       return INT(near);
00385    return dpi;
00386 }
00387 
00388 /*
00389  * Look for a PK <font> of resolution <dpi> in <path>. To avoid floating
00390  * around lots of different font sizes TeX prefers to work with whole
00391  * and half magsteps. So instead of using a 330 dpi font for a 11 point
00392  * version of a 300 dpi font we would prefer the 329 dpi size because
00393  * this size matches magstep 0.5 of 300 (329 = int(300 * 1.2 ** 0.5).
00394  * The process of truncating real font sizes to magsteps can be
00395  * controlled by the global parameter <tolerance> which may be an
00396  * expression of the actual value of <dpi> itself. The value dvips(1)
00397  * uses for <tolerance> is 1 + <dpi> / 500 allowing more tolerance in
00398  * bigger sizes. Due to round-off different DVI drivers choose different
00399  * <dpi> sizes. With <margin> we can define what font sizes are looked
00400  * at whenever size <dpi> is wanted. With <margin> set to zero only the
00401  * exact size is looked at, while a value of one would allow for a
00402  * round-off of one. Both <margin> and <tolerance> can be set before a
00403  * search starts otherwise they get their default values (zero) with
00404  * init_pksearch().
00405  *
00406  * When <path> does contain placeholders like %b, %d, %f, %p and %m
00407  * they will be substituted by their corresponding values _bdpi, <dpi>,
00408  * <font>, "pk" and _mode respectivally. This allows to define paths 
00409  * for a whole range of printers. Occurrences off ~ or * ~user in <path>
00410  * will be substituted by the corresponding HOME directories on UNIX.
00411  * 
00412  * The result of search_pkfile() is a pointer to the name of <font> when
00413  * one is found otherwise NULL.
00414  */
00415 char *search_pkfile(char *path, char *texfont, int dpi) {
00416    char *ppe, *pkpath, pe[MAXPATHLEN];
00417    int del;
00418    char *pkname;
00419    struct stat status;
00420 
00421    for (del=0; del <= _margin ; del=del>0?-del:-del+1) {
00422       pkpath = path;
00423       while (*pkpath) {
00424          /* copy next pathelement into pe */
00425          ppe = pe;
00426          while (*pkpath != PATHSEP && *pkpath != '\0') {
00427             if (tilde && *pkpath == '~')
00428                subtilde(&ppe, &pkpath);
00429             else
00430                *ppe++ = *pkpath++;
00431          }
00432          *ppe = '\0';
00433          if (strchr(pe, '%')) {
00434             if (strstr(pe, "%d") && strstr(pe, "%f")) {
00435                /* try filename after replacing placeholders */
00436                substitute(pe, "%b%m%f%d%p",
00437                 _bdpi, _mode, texfont, dpi+del, "pk");
00438               if (stat(pe, &status) == 0) {
00439                   pkname = malloc(strlen(pe)+1);
00440                   if (pkname == NULL) fatal("Out of memory\n");
00441                   strcpy(pkname, pe);
00442                   return pkname;
00443                }
00444             }
00445             else /* %f and %d are required! */
00446                fatal("%s: incomplete path element!\n", pe);
00447          }
00448          else {
00449            sprintf(ppe, "%c%s.%dpk", DIRSEP, texfont, dpi+del);
00450             if (stat(pe, &status) == 0) {
00451                pkname = malloc(strlen(pe)+1);
00452                if (pkname == NULL) fatal("Out of memory\n");
00453                strcpy(pkname, pe);
00454                return pkname;
00455             }
00456          }
00457          if (*pkpath == PATHSEP) pkpath++;
00458       }
00459    }
00460    return NULL;
00461 }
00462 
00463 /*
00464  * Functions for handling emTeX's FLI files.
00465  */
00466 
00467 /*
00468  * Look at the fonts in the font library by calling the function <font>.
00469  * This function can stop the search by returning a 1 or continue by
00470  * returning a zero. In this way we can look for just one font or for
00471  * a whole series of fonts. If the search is stopped the name of the 
00472  * current FLI file is returned otherwise a NULL pointer.
00473  */
00474 char *search_flifile(char *fliname, int (*font)(char *, int)) {
00475    unsigned short len, numsizes, numfonts, version1, version2;
00476    char fontname[50];
00477    int i, size;
00478 
00479    FILE *FLI;
00480 
00481    FLI = fopen(fliname, RB);
00482    if (FLI == NULL) return NULL;
00483 
00484    for (i=0; i<4; i++) {
00485       fontname[i] = one(FLI);  /* read header */
00486    }
00487    version1 = one(FLI);
00488    version2 = one(FLI);
00489    if (strncmp(fontname,"FLIB",4)!=0 || version1 != 2 || version2 != 0)
00490       fatal("incorrect font library format");
00491 
00492    (void) two(FLI);        /* ignore directory length */
00493    numsizes = two(FLI);    /* number of sizes */
00494    numfonts = two(FLI);    /* number of fonts */
00495    len = two(FLI);         /* length of comment */
00496    for (i=0; i<len; i++)
00497       (void)one(FLI);      /* skip comment */
00498 
00499    for ( ;numsizes>0; numsizes--) { 
00500       /* for each font size in this library */
00501       (void)two(FLI);      /* length of size entry - ignore */
00502       numfonts = two(FLI); /* number of fonts */
00503                         /* DPI (fixed point 16.16) */
00504       size = (int)(four(FLI)/65536.0+0.5);
00505  
00506       for ( ;numfonts > 0; numfonts--) {
00507          /* read each entry */
00508          (void)four(FLI);           /* ignore length of font */
00509          (void)four(FLI);           /* offset to font */
00510          len = one(FLI);            /* length of name */
00511          for (i=0; i<len; i++)
00512             fontname[i] = one(FLI);
00513          fontname[len] = '\0';
00514         if (font(fontname, size) == 1) {
00515             fclose(FLI);
00516            return fliname;
00517         };
00518       }
00519    }
00520    fclose(FLI);
00521    return NULL;
00522 }
00523 
00524 /*
00525  * Look for font libraries in <path> (ex.: c:\emtex\texfonts;fli_0;fli_1).
00526  * Run <font()> function for each .fli file found in <path>. Continue
00527  * if this function returns NULL, stop otherwise.
00528  */
00529 char *search_flipath(char *flipath, int (*font)(char *, int)) {
00530    char fn[MAXPATHLEN], *pfn, *pfli, *fliname;
00531    int len;
00532 
00533    if (flipath == NULL) fatal("Invalid fli path\n");
00534 
00535    /* treat first element of flipath as a directory */
00536    pfn = fn;
00537    while (*flipath != PATHSEP && *flipath != '\0') {
00538       *pfn++ = *flipath++;
00539    }
00540    *pfn = '\0'; len = strlen(fn);
00541    if (len >= 1 && fn[len-1] != DIRSEP) *pfn++ = DIRSEP;
00542 
00543    /* and next elements as fli files */
00544    while (*flipath != '\0') {
00545       /* copy next pathelement into fn */
00546       pfli = pfn; flipath++;
00547       while (*flipath != PATHSEP && *flipath != '\0') {
00548          *pfli++ = *flipath++;
00549       }
00550       *pfli = '\0'; len = strlen(pfn);
00551       if (len > 4 && strcmp(pfli-4, ".fli") != 0)
00552          strcat(pfli, ".fli");
00553       if ((fliname = search_flifile(fn, font)) != NULL)
00554         return fliname;
00555    }
00556    return NULL;
00557 }