summaryrefslogtreecommitdiff
path: root/dviware/umddvi/lib/seek.c
blob: 5f3274af651b7b867f70c75e9f308cb92943da9d (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
/*
 * Copyright (c) 1987 University of Maryland Department of Computer Science.
 * All rights reserved.  Permission to copy for any purpose is hereby granted
 * so long as this copyright notice remains intact.
 */

#ifndef lint
static char rcsid[] = "$Header: seek.c,v 2.4 87/06/16 18:29:03 chris Exp $";
#endif

/*
 * Seekable is a predicate which returns true iff a Unix fd is seekable.
 *
 * MakeSeekable forces an input stdio file to be seekable, by copying to
 * a temporary file if necessary.
 */

#include <stdio.h>
#ifdef sys5
#include <sys/types.h>
#include <sys/fcntl.h>
#else
#include <sys/param.h>
#endif
#include <sys/file.h>
#include <sys/stat.h>

long	lseek();
char	*getenv();

int
Seekable(fd)
	int fd;
{

	return (lseek(fd, 0L, 1) >= 0 && !isatty(fd));
}

/*
 * We use the despicable trick of unlinking an open temporary file.
 * The alternatives are too painful.  If it becomes necessary to
 * do this another way, however, here is a method suggested by Fred
 * Blonder: fork, and have the parent wait for the child to exit.
 * (The parent must avoid being killed during this phase.)  When
 * the child exits, the parent should then remove the temporary file,
 * then exit with the same status, or send itself the same signal.
 */
int
MakeSeekable(f)
	register FILE *f;
{
	register int tf, n;
	int mypid, tries, blksize;
	char *tmpdir;
#ifdef MAXBSIZE
	char buf[MAXBSIZE];
	struct stat st;
#else
	char buf[BUFSIZ];
#endif

	if (Seekable(fileno(f)))
		return (0);

	if ((tmpdir = getenv("TMPDIR")) == 0)
		tmpdir = "/tmp";

	/* compose a suitable temporary file name, and get an r/w descriptor */
	mypid = getpid();
	n = 0;
	tries = 0;
	do {
		sprintf(buf, "%s/#%d.%d", tmpdir, mypid, n++);
		(void) unlink(buf);
#ifdef O_CREAT			/* have three-argument open syscall */
		tries++;
		tf = open(buf, O_RDWR | O_CREAT | O_EXCL, 0666);
#else
		if (access(buf, 0) == 0) {
			/*
			 * Skip existing files.  Note that tf might
			 * not be set yet.
			 */
			tf = -1;
			continue;
		}
		tries++;
		tf = creat(buf, 0666);
		if (tf >= 0) {
			(void) close(tf);
			tf = open(buf, 2);
			if (tf < 0)
				(void) unlink(buf);
		}
#endif
	} while (tf < 0 && tries < 20);
	if (tf < 0)		/* probably unrecoverable user error */
		return (-1);

	(void) unlink(buf);

	/* copy from input file to temp file */
#ifdef MAXBSIZE
	if (fstat(tf, &st))	/* how can this ever fail? */
		blksize = MAXBSIZE;
	else
		blksize = MIN(MAXBSIZE, st.st_blksize);
#else
	blksize = BUFSIZ;
#endif
	while ((n = fread(buf, 1, blksize, f)) > 0) {
		if (write(tf, buf, n) != n) {
			(void) close(tf);
			return (-1);
		}
	}
	/* ferror() is broken in Ultrix 1.2; hence the && */
	if (ferror(f) && !feof(f)) {
		(void) close(tf);
		return (-1);
	}

	/*
	 * Now switch f to point at the temp file.  Since we hit EOF, there
	 * is nothing in f's stdio buffers, so we can play a dirty trick: 
	 */
	clearerr(f);
	if (dup2(tf, fileno(f))) {
		(void) close(tf);
		return (-1);
	}
	(void) close(tf);
	return (0);
}