home *** CD-ROM | disk | FTP | other *** search
/ Graphics Plus / Graphics Plus.iso / general / modelers / geomview / source.lha / Geomview / src / lib / oogl / util / futil.c < prev    next >
Encoding:
C/C++ Source or Header  |  1993-11-10  |  19.1 KB  |  849 lines

  1. /* Copyright (c) 1992 The Geometry Center; University of Minnesota
  2.    1300 South Second Street;  Minneapolis, MN  55454, USA;
  3.    
  4. This file is part of geomview/OOGL. geomview/OOGL is free software;
  5. you can redistribute it and/or modify it only under the terms given in
  6. the file COPYING, which you should have received along with this file.
  7. This and other related software may be obtained via anonymous ftp from
  8. geom.umn.edu; email: software@geom.umn.edu. */
  9. static char *copyright = "Copyright (C) 1992 The Geometry Center";
  10.  
  11. /* Authors: Charlie Gunn, Stuart Levy, Tamara Munzner, Mark Phillips */
  12.  
  13. /* $Header: /usrb/gcg/ngrap/src/lib/oogl/util/RCS/futil.c,v 1.25 1993/11/10 20:26:51 slevy Exp $ */
  14.  
  15. /*
  16.  * Geometry object routines
  17.  *
  18.  * Utility routines, useful for many objects
  19.  *
  20.  * int
  21.  * fgetnf(file, nfloats, floatp, binary)
  22.  *    Read an array of floats from a file in "ascii" or "binary" format.
  23.  *    Returns number of floats successfully read, should = nfloats.
  24.  *    "Binary" means "IEEE 32-bit floating-point" format.
  25.  *
  26.  * int
  27.  * fgetni(FILE *file, int nints, int *intsp, int binary)
  28.  *    Read an array of ints from a file in "ascii" or "binary" format.
  29.  *    Returns number of ints successfully read, should = nints.
  30.  *    "Binary" means "32-bit big-endian" integer format.
  31.  *
  32.  * int
  33.  * fgetns(FILE *file, int nshorts, short *intsp, int binary)
  34.  *    Read an array of shorts from a file in "ascii" or "binary" format.
  35.  *    Returns number of shorts successfully read, should = nints.
  36.  *    "Binary" means "16-bit big-endian" integer format.
  37.  *
  38.  * int
  39.  * fexpectstr(FILE *file, char *string)
  40.  *    Expect the given string to appear immediately on file.
  41.  *    Return 0 if the complete string is found,
  42.  *    else the offset+1 of the last matched char within string.
  43.  *    The first unmatched char is ungetc'd.
  44.  *
  45.  * int
  46.  * fexpecttoken(FILE *file, char *string)
  47.  *    Expect the given string to appear on the file, possibly after
  48.  *    skipping some white space and comments.
  49.  *    Return 0 if found, else the offset+1 of last matched char in string.
  50.  *    The first unmatched char is ungetc'd.
  51.  *
  52.  * int fnextc(FILE *f, int flags)
  53.  *    Advances f to the next "interesting" character and
  54.  *    returns it.  The returned char is ungetc'ed so the next getc()
  55.  *    will yield the same value.
  56.  *    Interesting depends on flags:
  57.  *      0 : Skip blanks, tabs, newlines, and comments (#...\n).
  58.  *      1 : Skip blanks, tabs, and comments, but newlines are interesting
  59.  *        (including the \n that terminates a comment).
  60.  *      2 : Skip blanks, tabs, and newlines, but stop at #.
  61.  *      3 : Skip blanks and tabs but stop at # or \n.
  62.  *
  63.  * int async_fnextc(FILE *f, int flags)
  64.  *    Like fnextc() above, but guarantees not to block if no data is
  65.  *    immediately available.  It returns either an interesting character,
  66.  *    EOF, or the special code NODATA (== -2).
  67.  *
  68.  * int async_getc(FILE *f)
  69.  *    Like getc(), but guarantees not to block.  Returns NODATA if
  70.  *    nothing is immediately available.
  71.  *
  72.  * char *ftoken(FILE *f, int flags)
  73.  *    Skips uninteresting characters with fnextc(f, flags),
  74.  *    then returns a "token" - string of consecutive interesting characters.
  75.  *    Returns NULL if EOF is reached with no token, or if
  76.  *    flags specifies stopping at end-of-line and this is encountered with
  77.  *    no token found.
  78.  *    The token is effectively statically allocated and will be
  79.  *    overwritten by the next ftoken() call.
  80.  *
  81.  * char *fdelimtok(char *delims, FILE *f, int flags)
  82.  *    Like ftoken(), but specially handles the characters in delims[].
  83.  *    If one appears at the beginning of the token, it's returned as 
  84.  *    a single-character token.
  85.  *    If a member of delims[] appears after other characters have been
  86.  *    accumulated, it's considered to terminate the token.
  87.  *    So successive calls to fdelimtok("()", f, 0)
  88.  *    on a file containing  (fred smith)
  89.  *    would return "(", "fred", "smith", and ")".
  90.  *    Behavior is undefined if one of the predefined delimiters
  91.  *    (white space or #) appears in delims[].  Explicit quoting
  92.  *    (with ", ' or \) overrides detection of delimiters.
  93.  *
  94.  * int fgettransform(FILE *f, int ntransforms, float *transforms, int binary)
  95.  *    Reads 4x4 matrices from FILE.  Returns the number of matrices found,
  96.  *    up to ntransforms.  Returns 0 if no numbers are found.
  97.  *    On finding incomplete matrices (not a multiple of 16 floats)
  98.  *    returns -1, regardless of whether any whole matrices were found.
  99.  *    Matrices are expected in the form used to transform a row vector
  100.  *    multiplied on the left, i.e.  p * T -> p'; thus Euclidean translations
  101.  *    appear in the last row.
  102.  *
  103.  * int fputtransform(FILE *f, int ntransforms, float *transforms, int binary)
  104.  *    Writes 4x4 matrices to FILE.  Returns the number written, i.e.
  105.  *    ntransforms unless an error occurs.
  106.  *
  107.  * int fputnf(FILE *f, int nfloats, float *fv, int binary)
  108.  *    Writes 'nfloats' floats to the given file.  ASCII is in %g format,
  109.  *    separated by blanks.
  110.  *
  111.  * FILE *fstropen(str, len, mode)
  112.  *    Opens a string (buffer) as a "file".
  113.  *    Mode is "r" or "w" as usual.
  114.  *    Reads should return EOF on encountering end-of-string,
  115.  *    writes past end-of-string should also yield an error return.
  116.  *    fclose() should be used to free the FILE after use.
  117.  */
  118.  
  119.  
  120. #include <stdio.h>
  121. #include <sys/types.h>
  122. #include <sys/time.h>
  123. #include <stdlib.h>
  124. #include <string.h>
  125. #ifdef sgi
  126. #include <unistd.h>
  127. #include <malloc.h>
  128. #endif /* -nils XXX */
  129. #include <ctype.h>
  130. #include "ooglutil.h"
  131.  
  132. int
  133. fnextc(FILE *f, int flags)
  134. {
  135.     register int c;
  136.  
  137.     c = getc(f);
  138.     for(;;) {
  139.         switch(c) {
  140.         case EOF:
  141.         return(EOF);
  142.  
  143.         case ' ':
  144.         case '\t':
  145.         break;            /* Always skip blanks and tabs */
  146.  
  147.         case '#':
  148.         if(flags & 2)        /* 2: stop on comments, else skip */
  149.             goto fim;
  150.  
  151.         while((c = getc(f)) != '\n' && c != EOF)
  152.             ;
  153.         continue;        /* Rescan this c */
  154.  
  155.         case '\n':
  156.         if(!(flags & 1))    /* 1: stop on \n's, else skip them */
  157.             break;
  158.                     /* flags&1 => fall into default */
  159.  
  160.         default:
  161.          fim:
  162.         ungetc(c, f);
  163.         return(c);
  164.         }
  165.  
  166.         c = getc(f);
  167.     }
  168. }
  169.  
  170.  
  171. float f_pow10[11] = { 1e0, 1e1, 1e2, 1e3, 1e4, 1e5, 1e6, 1e7, 1e8, 1e9, 1e10 };
  172.  
  173. #define fPow10(n)  ((unsigned)(n) > 10 ? fpow10(n) : f_pow10[n])
  174.  
  175. float
  176. fpow10(int n)
  177. {
  178.     register int k;
  179.     register float v;
  180.  
  181.     if((k = n) < 0)
  182.         k = -k;
  183.     v = f_pow10[k & 7];
  184.     if(k >= 8) {
  185.         float unit = 1e8;
  186.  
  187.         k >>= 3;
  188.         for(;;) {
  189.             if(k & 1)
  190.                 v *= unit;
  191.             if((k >>= 1) == 0)
  192.                 break;
  193.             unit *= unit;
  194.         }
  195.     }
  196.     return(n < 0 ? 1.0/v : v);
  197. }
  198.  
  199. /*
  200.  * Read an array of white-space-separated floats from file 'f' in ASCII, fast.
  201.  * Returns the number successfully read.
  202.  */
  203. int
  204. fgetnf(register FILE *f, int maxf, float *fv, int binary)
  205. {
  206.     int ngot;
  207.     float v;
  208.     register int c = EOF;
  209.     register long n;
  210.     long w;
  211.     int s, es, nd, any;
  212.  
  213.     if(binary) {
  214. #if m68k || mc68000 || mips || sparc
  215.         /* Easy -- our native floating point == big-endian IEEE */
  216.         return fread((char *)fv, sizeof(float), maxf, f);
  217. #else /* not native big-endian IEEE */
  218.         for(n=0; n<maxf && fread((char *)&w,sizeof(long),1,f) > 0; n++)
  219.             *(long *)&fv[n] = ntohl(w);
  220.         return n;
  221. #endif /* not native big-endian IEEE */
  222.     }
  223.  
  224.     /* Read ASCII format floats */
  225.     for(ngot = 0; ngot < maxf; ngot++) {
  226.         if(fnextc(f, 0) == EOF)
  227.             return(ngot);
  228.         n = 0;
  229.         s = 0;
  230.         nd = 0;
  231.         any = 0;
  232.         if((c = getc(f)) == '-') {
  233.             s = 1;
  234.             c = getc(f);
  235.         }
  236.         while(c >= '0' && c <= '9') {
  237.             n = n*10 + c - '0';
  238.             nd++;
  239.             if(n >= 214748364) {    /* 2^31 / 10 */
  240.                 v = any ? v*fPow10(nd) + (float)n : (float)n;
  241.                 nd = n = 0;
  242.                 any = 1;
  243.             }
  244.             c = getc(f);
  245.         }
  246.         v = any ? v*fPow10(nd) + (float)n : (float)n;
  247.         any += nd;
  248.         if(c == '.') {
  249.             nd = n = 0;
  250.             while((c = getc(f)) >= '0' && c <= '9') {
  251.                 n = n*10 + c - '0';
  252.                 nd++;
  253.                 if(n >= 214748364) {
  254.                     v += (float)n / fPow10(nd);
  255.                     n = 0;
  256.                 }
  257.             }
  258.             v += (float)n / fPow10(nd);
  259.         }
  260.         if(any == 0 && nd == 0)
  261.             break;
  262.         if(c == 'e' || c == 'E') {
  263.             es = nd = 0;
  264.             switch(c = getc(f)) {
  265.             case '-':
  266.                 es = 1;    /* And fall through */
  267.             case '+':
  268.                 c = getc(f);
  269.             }
  270.             n = 0;
  271.             while(c >= '0' && c <= '9') {
  272.                 n = n*10 + c - '0';
  273.                 nd++;
  274.                 c = getc(f);
  275.             }
  276.             if(nd == 0)
  277.                 break;
  278.             if(es) v /= fPow10(n);
  279.             else v *= fPow10(n);
  280.         }
  281.         fv[ngot] = s ? -v : v;
  282.     }
  283.     if(c!=EOF) ungetc(c, f);
  284.     return(ngot);
  285. }
  286.  
  287.  
  288. int
  289. fgetni(register FILE *f, int maxi, int *iv, int binary)
  290. {
  291.     int ngot;
  292.     register int c = EOF;
  293.     register long n;
  294.     long w;
  295.     int s, any;
  296.  
  297.     if(binary) {
  298. #if m68k || mc68000 || mips || sparc
  299.         /* Easy -- our native floating point == big-endian IEEE */
  300.         return fread((char *)iv, sizeof(int), maxi, f);
  301. #else /* not native big-endian int's */
  302.         for(n = 0; n < maxi && fread(&w,4,1,f) > 0; n++)
  303.             iv[n] = ntohl(w);
  304.         return n;
  305. #endif /* not native big-endian int's */
  306.     }
  307.  
  308.     /* Read ASCII format floats */
  309.     for(ngot = 0; ngot < maxi; ngot++) {
  310.         if(fnextc(f, 0) == EOF)
  311.             return(ngot);
  312.         n = 0;
  313.         s = 0;
  314.         any = 0;
  315.         if((c = getc(f)) == '-') {
  316.             s = 1;
  317.             c = getc(f);
  318.         }
  319.         while(c >= '0' && c <= '9') {
  320.             n = n*10 + c - '0';
  321.             any = 1;
  322.             c = getc(f);
  323.         }
  324.         if(!any)
  325.             break;
  326.         iv[ngot] = s ? -n : n;
  327.     }
  328.     if(c!=EOF) ungetc(c, f);
  329.     return(ngot);
  330. }
  331.  
  332. int
  333. fgetns(register FILE *f, int maxs, short *sv, int binary)
  334. {
  335.     int ngot;
  336.     register int c = EOF;
  337.     register long n;
  338.     short w;
  339.     int s, any;
  340.  
  341.     if(binary) {
  342. #if m68k || mc68000 || mips || sparc
  343.         /* Easy -- our native floating point == big-endian IEEE */
  344.         return fread((char *)sv, sizeof(short), maxs, f);
  345. #else /* not native big-endian int's */
  346.         for(n = 0; n < maxs && fread(&w,2,1,f) > 0; n++)
  347.             sv[n] = ntohs(w);
  348.         return n;
  349. #endif /* not native big-endian int's */
  350.     }
  351.  
  352.     /* Read ASCII format floats */
  353.     for(ngot = 0; ngot < maxs; ngot++) {
  354.         if(fnextc(f, 0) == EOF)
  355.             return(ngot);
  356.         n = s = any = 0;
  357.         if((c = getc(f)) == '-') {
  358.             s = 1;
  359.             c = getc(f);
  360.         }
  361.         while(c >= '0' && c <= '9') {
  362.             n = n*10 + c - '0';
  363.             any = 1;
  364.             c = getc(f);
  365.         }
  366.         if(!any)
  367.             break;
  368.         sv[ngot] = s ? -n : n;
  369.     }
  370.     if(c!=EOF) ungetc(c, f);
  371.     return(ngot);
  372. }
  373.  
  374. /*
  375.  * Check for a string on a file.
  376.  * If found, return 0.
  377.  * If not, return the offset of the last matched char +1
  378.  * and ungetc the failed char so the caller can see it.
  379.  */
  380. int
  381. fexpectstr(register FILE *file, char *str)
  382. {
  383.     register char *p = str;
  384.     register int c;
  385.  
  386.     while(*p != '\0') {
  387.         if((c = getc(file)) != *p++) {
  388.         if(c != EOF)
  389.             ungetc(c, file);
  390.         return(p - str);
  391.         }
  392.     }
  393.     return 0;
  394. }
  395.  
  396. /*
  397.  * Check for a string on a file, skipping leading blanks.
  398.  */
  399. int
  400. fexpecttoken(register FILE *file, char *str)
  401. {
  402.     (void) fnextc(file, 0);
  403.     return fexpectstr(file, str);
  404. }
  405.  
  406. int fescape(FILE *f)
  407. {
  408.     int n, k, c = fgetc(f);
  409.  
  410.     switch(c) {
  411.     case 'n': return '\n';
  412.     case 'b': return '\b';
  413.     case 't': return '\t';
  414.     case 'r': return '\r';
  415.     }
  416.     if(c < '0' || c > '7')
  417.     return c;
  418.     
  419.     n = c-'0';  k = 2;
  420.     while((c = fgetc(f)) >= '0' && c <= '7') {
  421.     n = n*8 | c-'0';
  422.     if(--k <= 0)
  423.         return n;
  424.     }
  425.     if(c != EOF) ungetc(c, f);
  426.     return n;
  427. }
  428.  
  429. /*
  430.  * Get a token, return a string or NULL.
  431.  * Tokens may be "quoted" or 'quoted'; backslashes accepted.
  432.  * The string is statically allocated and should be copied if
  433.  * needed before the next call to ftoken().
  434.  */
  435. char *
  436. ftoken(FILE *file, int flags)
  437. {
  438.     static char *token = NULL;
  439.     static int troom = 0;
  440.     register int c;
  441.     register char *p;
  442.     register int term;
  443.  
  444.     if((term = fnextc(file, flags)) == EOF)
  445.         return NULL;
  446.  
  447.     if(token == NULL) {
  448.         troom = 50;
  449.         token = malloc(troom * sizeof(char));
  450.         if(token == NULL)
  451.         return NULL;
  452.     }
  453.  
  454.     p = token;
  455.     switch(term) {
  456.     case '"':
  457.     case '\'':
  458.         (void) fgetc(file);
  459.         for(;;) { 
  460.         if((c = getc(file)) == EOF || c == term)
  461.             break;
  462.         else if(c == '\\')
  463.             c = fescape(file);
  464.         *p++ = c;
  465.         if(p == &token[troom]) {
  466.             token = realloc(token, troom * 2);
  467.             if(token == NULL)
  468.             return NULL;
  469.             p = &token[troom];
  470.             troom *= 2;
  471.         }
  472.         }
  473.         break;
  474.  
  475.     default:
  476.         if(isspace(term))
  477.         return NULL;
  478.         while((c = getc(file)) != EOF && !isspace(c)) {
  479.         if(c == '\\')
  480.             c = fescape(file);
  481.         *p++ = c;
  482.         if(p == &token[troom]) {
  483.             token = realloc(token, troom * 2);
  484.             if(token == NULL)
  485.             return NULL;
  486.             p = &token[troom];
  487.             troom *= 2;
  488.         }
  489.         }
  490.         break;
  491.     }
  492.     *p = '\0';
  493.     return token;
  494. }
  495.  
  496.  
  497. /*
  498.  * Get a token, return a string or NULL.
  499.  * Tokens may be "quoted" or 'quoted'; backslashes accepted.
  500.  * The string is statically allocated and should be copied if
  501.  * needed before the next call to ftoken().
  502.  */
  503. char *
  504. fdelimtok(char *delims, FILE *file, int flags)
  505. {
  506.     static char *token = NULL;
  507.     static int troom = 0;
  508.     register int c;
  509.     register char *p;
  510.     register char *q;
  511.     register int term;
  512.  
  513.     if((term = fnextc(file, flags)) == EOF)
  514.         return NULL;
  515.  
  516.     if(token == NULL) {
  517.         troom = 50;
  518.         token = malloc(troom * sizeof(char));
  519.         if(token == NULL)
  520.         return NULL;
  521.     }
  522.  
  523.     p = token;
  524.     switch(term) {
  525.     case '"':
  526.     case '\'':
  527.         (void) fgetc(file);
  528.         for(;;) { 
  529.         if((c = getc(file)) == EOF || c == term)
  530.             break;
  531.         else if(c == '\\')
  532.             c = fescape(file);
  533.         *p++ = c;
  534.         if(p == &token[troom]) {
  535.             token = realloc(token, troom * 2);
  536.             if(token == NULL)
  537.             return NULL;
  538.             p = &token[troom];
  539.             troom *= 2;
  540.         }
  541.         }
  542.         break;
  543.  
  544.     default:
  545.         if(isspace(term))
  546.         return NULL;
  547.         while((c = getc(file)) != EOF && !isspace(c)) {
  548.         if(c == '\\')
  549.             c = fescape(file);
  550.         *p = c;
  551.         if(++p == &token[troom]) {
  552.             token = realloc(token, troom * 2);
  553.             if(token == NULL)
  554.             return NULL;
  555.             p = &token[troom];
  556.             troom *= 2;
  557.         }
  558.         for(q = delims; *q && c != *q; q++)
  559.             ;
  560.         if(*q) {
  561.             if(p > token+1) {
  562.             p--;
  563.             ungetc(c, file);
  564.             }
  565.             break;
  566.         }
  567.         }
  568.         break;
  569.     }
  570.     *p = '\0';
  571.     return token;
  572. }
  573.  
  574.  
  575. /*
  576.  * Load one or more Transforms from a file.
  577.  * Return 1 on success, 0 on failure.
  578.  */
  579. int
  580. fgettransform(FILE *file, int ntrans, float *trans /* float trans[ntrans][4][4] */, int binary)
  581. {
  582.     register float *T;
  583.     int nt;
  584.  
  585.     for(nt = 0; nt < ntrans; nt++) {
  586.         T = trans + 16*nt;
  587.         switch(fgetnf(file, 16, T, binary)) {
  588.         case 16:
  589.         break;
  590.  
  591.         case 0:
  592.         return nt;
  593.  
  594.         default:
  595.         return -1;
  596.         }
  597.     }
  598.     return ntrans;
  599. }
  600.  
  601. int
  602. fputnf(FILE *file, int count, float *v, int binary)
  603. {
  604.     register int i;
  605.     long w;
  606.     if(binary) {
  607. #if m68k || mc68000 || mips || sparc
  608.       return fwrite(v, sizeof(float), count, file);
  609. #else
  610.       for(i = 0; i < count; i++) {
  611.         w = htonl(*(long *)&v[i]);
  612.         fwrite(&w, sizeof(float), 1, file);
  613.       }
  614.       return count;
  615. #endif
  616.     }
  617.  
  618.     fprintf(file, "%g", v[0]);
  619.     for(i = 1; i < count; i++)
  620.         fprintf(file, " %g", v[i]);
  621.     return count;
  622. }
  623.  
  624. int
  625. fputtransform(FILE *file, int ntrans, float *trans, int binary)
  626. {
  627.     register int i, n;
  628.     register float *p;
  629.  
  630.     if(binary) {
  631. #if m68k || mc68000 || mips || sparc
  632.         return fwrite(trans, 4*4*sizeof(float), ntrans, file);
  633. #else
  634.     OOGLError(1, "fputtransform: need code to handle binary writes for this architecture.");
  635.     return 0;
  636. #endif
  637.     }
  638.  
  639.     /* ASCII. */
  640.  
  641.     for(n = 0; n < ntrans; n++) {
  642.         p = trans + n*16;
  643.         for(i = 0; i < 4; i++, p += 4) {
  644.         fprintf(file, "  %12.8g  %12.8g  %12.8g  %12.8g\n",
  645.             p[0], p[1], p[2], p[3]);
  646.         }
  647.         if(ferror(file))
  648.         return n;
  649.         fprintf(file, "\n");
  650.     }
  651.     return ntrans;
  652. }
  653.  
  654. /*
  655.  * Given a file pointer, return a string attempting to show the context
  656.  * of its current position.  If no data is available, returns the empty string.
  657.  */
  658. char *
  659. fcontext(register FILE *f)
  660. {
  661.     static char *cont = NULL;
  662.     static char dflt[] = "";
  663.     char buf[1024];
  664.     int npre, npost, nlpre, nlpost, tab, len;
  665.     int predots = 4, postdots = 4;
  666.     register char *p, *q;
  667.     char *lastline, *lastnonblank;
  668.  
  669.     if(f == NULL)
  670.     return dflt;
  671.     if(feof(f))
  672.     return "> END OF FILE\n";
  673.     if(f->_cnt <= 0 || f->_base == NULL || f->_ptr == NULL)
  674.     return dflt;
  675.  
  676.     p = (char *)f->_ptr;
  677.     for(npre = nlpre = 0; --p >= (char *)f->_base && npre < 128; npre++) {
  678.     if(*p == '\n') {
  679.         if(++nlpre > 2 || npre > 60) {
  680.         predots = 0;
  681.         break;
  682.         }
  683.     } else if(*p & 0x80 || *p == 0)        /* binary data? */
  684.         break;
  685.     }
  686.     strcpy(buf, "> ... ");
  687.     q = buf + 2 + predots;
  688.     tab = 2 + predots;
  689.     for(p = (char *)f->_ptr - npre; p < (char *)f->_ptr; ) {
  690.     switch(*q++ = *p++) {
  691.     case '\n': case '\r':    *q++ = '>'; *q++ = ' '; tab = 2; break;
  692.     case '\t':        tab += 8-(tab&7); break;
  693.     default:        tab++;
  694.     }
  695.     }
  696.     len = npre;
  697.     npost = nlpost = 0;
  698.     lastline = lastnonblank = q;
  699.     for(p = (char *)f->_ptr;  p < (char *)f->_ptr + f->_cnt && len < 128;  len++, q++) {
  700.     *q = *p++;
  701.     if(*q == '\n') {
  702.         if(nlpost == 0) {
  703.         while(--tab > 0) *++q = '-';    /* Point ---^ to error */
  704.         *++q = '^'; *++q = '\n';
  705.         }
  706.         if((++nlpost >= 2 || len > 80) && len > 32) {
  707.         postdots = 0;
  708.         break;
  709.         }
  710.         lastline = q;
  711.         *++q = '>'; *++q = ' ';
  712.     } else if(*q & 0x80 || *q == 0)        /* Binary data */
  713.         break;
  714.     else if(isprint(*q))
  715.         lastnonblank = q;
  716.     }
  717.     if(postdots && lastnonblank < lastline) {
  718.     q = lastline;        /* Suppress trailing white space */
  719.     postdots = 0;        /* to avoid final ``> ...'' */
  720.     }
  721.     strcpy(q, " ...\n" + 4-postdots);
  722.     if(nlpost == 0) {
  723.     q += strlen(q);
  724.     while(--tab > 0) *q++ = '-';
  725.     strcpy(q, "^\n");
  726.     }
  727.     if(cont) free(cont);
  728.     return (cont = strdup(buf));
  729. }
  730.  
  731.  
  732. /*
  733.  * This one is stdio dependent, but will probably work on a fair variety of
  734.  * implementations.
  735.  */
  736. FILE *
  737. fstropen(char *str, int len, char *mode)
  738. {
  739.     FILE *f;
  740.  
  741.     f = fopen("/dev/null", mode);    /* How else do I get a FILE *? */
  742.     if(f == NULL)
  743.         return NULL;
  744.     setbuf(f, NULL);
  745.     close(fileno(f));
  746.  
  747.     f->_file = -1;
  748.     f->_cnt = len;
  749.     f->_ptr = f->_base =
  750.       (unsigned char *)
  751. #if defined(sgi) || defined(sun4)    /* SGI/MIPS Nazi C compiler! */
  752.         (unsigned char *)
  753. #endif
  754.         str;
  755.     f->_flag &= ~(_IONBF|_IOMYBUF|_IOEOF|_IOERR);
  756.     return f;
  757. }
  758.  
  759. struct stdio_mark {
  760.     FILE *f;
  761.     long fpos;
  762.     FILE fcopy;
  763.     char fchars[8];
  764. };
  765.  
  766. struct stdio_mark *
  767. stdio_setmark(register struct stdio_mark *mark, register FILE *stream)
  768. {
  769.     if(mark == NULL)
  770.     mark = OOGLNewE(struct stdio_mark, "stdio_setmark mark");
  771.     mark->f = stream;
  772.     mark->fpos = isatty(fileno(stream)) ? -1 : ftell(stream);
  773.     mark->fcopy = *stream;
  774.     memcpy(mark->fchars, mark->f->_base, sizeof(mark->fchars));
  775.     return mark;
  776. }
  777.  
  778. int
  779. stdio_seekmark(register struct stdio_mark *mark)
  780. {
  781.     if(mark->fpos == -1 || fseek(mark->f, mark->fpos, 0) == -1) {
  782.     /* Maybe it'mark a pipe or socket, so seeks fail.
  783.      * Try repositioning inside the stdio buffer.
  784.      * This is a stdio-dependent kludge, but might work.
  785.      */
  786.     if(mark->f->_base == mark->fcopy._base &&
  787.        memcmp(mark->fchars, mark->f->_base, sizeof(mark->fchars)) == 0) {
  788.             /* Buffer looks unchanged.  Reset pointers. */
  789.         *(mark->f) = mark->fcopy;
  790.     } else {
  791.         return 0;    /* Failed */
  792.     }
  793.     }
  794.     return 1;        /* Succeeded */
  795. }
  796.  
  797. int
  798. async_getc(register FILE *f)
  799. {
  800.     fd_set fds;
  801.     static struct timeval notime = { 0, 0 };
  802.     if(f->_cnt > 0)
  803.     return getc(f);
  804.     FD_ZERO(&fds);
  805.     FD_SET(fileno(f), &fds);
  806.     if(select(fileno(f)+1, &fds, NULL, NULL, ¬ime) == 1)
  807.     return fgetc(f);
  808.     return NODATA;
  809. }
  810.  
  811. int
  812. async_fnextc(register FILE *f, register int flags)
  813. {
  814.     register int c;
  815.  
  816.     c = async_getc(f);
  817.     for(;;) {
  818.     switch(c) {
  819.     case EOF:
  820.     case NODATA:
  821.         return(c);
  822.  
  823.     case ' ':
  824.     case '\t':
  825.         break;            /* Always skip blanks and tabs */
  826.  
  827.     case '#':
  828.         if(flags & 2)        /* 2: stop on comments, else skip */
  829.         goto fim;
  830.  
  831.         while((c = getc(f)) != '\n' && c != EOF)
  832.         ;
  833.         continue;            /* Rescan this c */
  834.  
  835.     case '\n':
  836.         if(!(flags & 1))        /* 1: stop on \n's, else skip them */
  837.         break;
  838.                         /* flags&1 => fall into default */
  839.  
  840.     default:
  841.      fim:
  842.         ungetc(c, f);
  843.         return(c);
  844.     }
  845.  
  846.     c = async_getc(f);
  847.     }
  848. }
  849.