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