Manual is in section 8, you MUST say MAN8= for this to work. Add $Id$
[unix-history] / sbin / ft / ft.c
CommitLineData
9815cb4a
AM
1/*
2 * Copyright (c) 1993 Steve Gerakines
3 *
4 * This is freely redistributable software. You may do anything you
5 * wish with it, so long as the above notice stays intact.
6 *
7 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR(S) ``AS IS'' AND ANY EXPRESS
8 * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
9 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
10 * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR(S) BE LIABLE FOR ANY DIRECT,
11 * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
12 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
13 * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
14 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
15 * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
16 * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
17 * POSSIBILITY OF SUCH DAMAGE.
18 *
19 * ft.c - simple floppy tape filter
20 *
21 * 01/28/94 v0.3b (Jim Babb)
22 * Fixed bug when all sectors in a segment are marked bad.
23 *
24 * 10/30/93 v0.3
25 * Minor revisions. Seems pretty stable.
26 *
27 * 09/02/93 v0.2 pl01
28 * Initial revision.
29 *
30 * usage: ftfilt [ -f tape ] [ description ]
31 */
32
33#include <stdio.h>
34#include <stdlib.h>
35#include <string.h>
36#include <signal.h>
37#include <time.h>
38#include <sys/ftape.h>
39
40#define DEFQIC "/dev/rft0"
41
42char buff[QCV_SEGSIZE]; /* scratch buffer */
43char hbuff[QCV_SEGSIZE]; /* header buffer */
44QIC_Header *hptr = (QIC_Header *)hbuff; /* header structure */
45int hsn = -1; /* segment number of header */
46int dhsn = -1; /* segment number of duplicate header */
47int tfd; /* tape file descriptor */
48QIC_Geom geo; /* tape geometry */
49int tvno = 1; /* tape volume number */
50int tvlast; /* TRUE if last volume in set */
51long tvsize = 0; /* tape volume size in bytes */
52long tvtime = NULL; /* tape change time */
53char *tvnote = ""; /* tape note */
54
55/* Lookup the badmap for a given track and segment. */
56#define BADMAP(t,s) hptr->qh_badmap[(t)*geo.g_segtrk+(s)]
57
58/* Retrieve values from a character array. */
59#define UL_VAL(s,p) (*((ULONG *)&(s)[p]))
60#define US_VAL(s,p) (*((USHORT *)&(s)[p]))
61
62#define equal(s1,s2) (strcmp(s1, s2) == 0)
63
64
65
66/* Entry */
67main(int argc, char *argv[])
68{
69 int r, s;
70 char *tape, *getenv();
71
72 if (argc > 2 && (equal(argv[1], "-t") || equal(argv[1], "-f"))) {
73 argc -= 2;
74 tape = argv[2];
75 argv += 2;
76 } else
77 if ((tape = getenv("TAPE")) == NULL)
78 tape = DEFQIC;
79 if (argc > 1) {
80 tvnote = argv[1];
81 if (strlen(tvnote) > 18) argv[1][18] = '\0';
82 }
83
84 /* Open the tape device */
85 if ((tfd = open(tape, 2)) < 0) {
86 perror(tape);
87 exit(1);
88 }
89
90 if (!isatty(0))
91 do_write();
92 else if (!isatty(1))
93 do_read();
94 else
95 do_getname();
96
97 close(tfd);
98 exit(0);
99}
100
101
102/* Check status of tape drive */
103int check_stat(int fd, int wr)
104{
105 int r, s;
106 int sawit = 0;
107
108 /* get tape status */
109 if (ioctl(fd, QIOSTATUS, &s) < 0) {
110 fprintf(stderr, "could not get drive status\n");
111 return(1);
112 }
113
114 /* wait for the tape drive to become ready */
115 while ((s & QS_READY) == 0) {
116 if (!sawit) {
117 fprintf(stderr, "waiting for drive to become ready...\n");
118 sawit = 1;
119 }
120 sleep(2);
121 if (ioctl(fd, QIOSTATUS, &s) < 0) {
122 fprintf(stderr, "could not get drive status\n");
123 return(1);
124 }
125 }
126
127 if ((s & QS_FMTOK) == 0) {
128 fprintf(stderr, "tape is not formatted\n");
129 return(2);
130 }
131
132 if (wr && (s & QS_RDONLY) != 0) {
133 fprintf(stderr, "tape is write protected\n");
134 return(3);
135 }
136
137 return(0);
138}
139
140
141
142ULONG qtimeval(time_t t)
143{
144 struct tm *tp;
145 ULONG r;
146
147 tp = localtime(&t);
148 r = 2678400 * tp->tm_mon +
149 86400 *(tp->tm_mday-1) +
150 3600 * tp->tm_hour +
151 60 * tp->tm_min +
152 tp->tm_sec;
153 r |= (tp->tm_year - 70) << 25;
154 return(r);
155}
156
157/* Return tm struct from QIC date format. */
158struct tm *qtime(UCHAR *qt)
159{
160 ULONG *vp = (ULONG *)qt;
161 struct tm t;
162 ULONG v;
163 time_t tv;
164
165 v = *vp;
166 t.tm_year = ((v >> 25) & 0x7f)+70; v &= 0x1ffffff;
167 t.tm_mon = v / 2678400; v %= 2678400;
168 t.tm_mday = v / 86400 + 1; v %= 86400;
169 t.tm_hour = v / 3600; v %= 3600;
170 t.tm_min = v / 60; v %= 60;
171 t.tm_sec = v;
172 t.tm_wday = 0; /* XXX - let mktime do the real work */
173 t.tm_yday = 0;
174 t.tm_isdst = 0;
175 t.tm_gmtoff = 0;
176 t.tm_zone = NULL;
177 tv = mktime(&t);
178 return(localtime(&tv));
179}
180
181/* Return a string, zero terminated */
182char *qstr(char *str, int nchar)
183{
184 static char tstr[256];
185 strncpy(tstr, str, nchar);
186 tstr[nchar] = '\0';
187 return(tstr);
188}
189
190/* Read header from tape */
191get_header(int fd)
192{
193 int r, sn, bytes;
194 QIC_Segment s;
195 int gothdr = 0;
196
197 if (ioctl(fd, QIOGEOM, &geo) < 0) {
198 fprintf(stderr, "couldn't determine tape geometry\n");
199 return(1);
200 }
201
202 /* Get the header and duplicate */
203 for (sn = 0; sn < 16; sn++) {
204 s.sg_trk = 0;
205 s.sg_seg = sn;
206 s.sg_badmap = 0;
207 s.sg_data = (UCHAR *)&buff[0];
208 ioctl(fd, QIOREAD, &s);
209 r = check_parity(s.sg_data, 0, s.sg_crcmap);
210 if (s.sg_data[0] == 0x55 && s.sg_data[1] == 0xaa &&
211 s.sg_data[2] == 0x55 && s.sg_data[3] == 0xaa) {
212 if (hsn >= 0) {
213 dhsn = sn;
214 if (!r && !gothdr) {
215 fprintf(stderr, "using secondary header\n");
216 bcopy(s.sg_data, hbuff, QCV_SEGSIZE);
217 gothdr = 1;
218 }
219 break;
220 }
221 hsn = sn;
222 if (!r) {
223 bcopy(s.sg_data, hbuff, QCV_SEGSIZE);
224 gothdr = 1;
225 } else {
226 fprintf(stderr, "too many errors in primary header\n");
227 }
228 }
229 }
230
231 if (!gothdr) {
232 fprintf(stderr, "couldn't read header segment\n");
233 ioctl(fd, QIOREWIND);
234 return(1);
235 }
236
237 return(0);
238}
239
240
241ask_vol(int vn)
242{
243 FILE *inp;
244 int fd;
245 char c;
246
247 if ((fd = open("/dev/tty", 2)) < 0) {
248 fprintf(stderr, "argh!! can't open /dev/tty\n");
249 exit(1);
250 }
251
252 fprintf(stderr, "Insert ftfilt volume %02d and press enter:", vn);
253 read(fd, &c, 1);
254 close(fd);
255}
256
257
258/* Return the name of the tape only. */
259do_getname()
260{
261 if (check_stat(tfd, 0)) exit(1);
262 if (get_header(tfd)) exit(1);
263 fprintf(stderr, "\"%s\" - %s",
264 qstr(hptr->qh_tname,44), asctime(qtime(hptr->qh_chgdate)));
265 ioctl(tfd, QIOREWIND);
266}
267
268
269/* Extract data from tape to stdout */
270do_read()
271{
272 int sno, vno, sbytes, r;
273 long curpos;
274 char *hname;
275 QIC_Segment s;
276
277 /* Process multiple volumes if necessary */
278 vno = 1;
279 for (;;) {
280 if (check_stat(tfd, 0)) {
281 ask_vol(vno);
282 continue;
283 }
284 if (get_header(tfd)) {
285 ask_vol(vno);
286 continue;
287 }
288
289 /* extract volume and header info from label */
290 hname = hptr->qh_tname;
291 hname[43] = '\0';
292 tvno = atoi(&hname[11]);
293 tvlast = (hname[10] == '*') ? 1 : 0;
294 tvsize = atoi(&hname[14]);
295 tvnote = &hname[25];
296 if (vno != tvno || strncmp(hname, "ftfilt", 6) != 0) {
297 fprintf(stderr, "Incorrect volume inserted. This tape is:\n");
298 fprintf(stderr,"\"%s\" - %s\n", hname,
299 asctime(qtime(hptr->qh_chgdate)));
300 ask_vol(vno);
301 continue;
302 }
303
304 /* Process this volume */
305 curpos = 0;
306 for (sno = hptr->qh_first; tvsize > 0; sno++) {
307 s.sg_trk = sno / geo.g_segtrk;
308 s.sg_seg = sno % geo.g_segtrk;
309 s.sg_badmap = BADMAP(s.sg_trk,s.sg_seg);
310 sbytes = sect_bytes(s.sg_badmap) - QCV_ECCSIZE;
311 s.sg_data = (UCHAR *)&buff[0];
312
313 /* skip segments with *all* sectors flagged as bad */
314 if (sbytes > 0) {
315 if (ioctl(tfd, QIOREAD, &s) < 0) perror("QIOREAD");
316 r = check_parity(s.sg_data, s.sg_badmap, s.sg_crcmap);
317 if (r) fprintf(stderr, "** warning: ecc failed at byte %ld\n",
318 curpos);
319 if (tvsize < sbytes) sbytes = tvsize;
320 write(1, s.sg_data, sbytes);
321 tvsize -= sbytes;
322 curpos += sbytes;
323 }
324 }
325 if (tvlast) break;
326 ioctl(tfd, QIOREWIND);
327 ask_vol(++vno);
328 }
329 ioctl(tfd, QIOREWIND);
330 return(0);
331}
332
333
334/* Dump data from stdin to tape */
335do_write()
336{
337 int sno, vno, amt, sbytes;
338 int c, maxseg, r;
339 ULONG qnow;
340 QIC_Segment s;
341 char tmpstr[80];
342
343 qnow = qtimeval(time(NULL));
344 vno = 1;
345
346 for (;;) {
347 if (check_stat(tfd, 1)) {
348 ask_vol(vno);
349 continue;
350 }
351 if (get_header(tfd)) {
352 ask_vol(vno);
353 continue;
354 }
355
356 maxseg = geo.g_segtrk * geo.g_trktape - 1;
357 sno = hptr->qh_first;
358 tvno = vno;
359 tvsize = 0;
360 tvlast = 0;
361
362 /* Process until end of volume or end of data */
363 for (sno = hptr->qh_first; sno < maxseg && tvlast == 0; ++sno) {
364 /* Prepare to load the next segment */
365 s.sg_trk = sno / geo.g_segtrk;
366 s.sg_seg = sno % geo.g_segtrk;
367 s.sg_badmap = BADMAP(s.sg_trk,s.sg_seg);
368 sbytes = sect_bytes(s.sg_badmap) - QCV_ECCSIZE;
369 s.sg_data = (UCHAR *)&buff[0];
370
371 /* Ugh. Loop to get the full amt. */
372 for (amt = 0; amt < sbytes; amt += r) {
373 r = read(0, &s.sg_data[amt], sbytes - amt);
374 if (r <= 0) {
375 tvlast = 1;
376 break;
377 }
378 }
379 /* skip the segment if *all* sectors are flagged as bad */
380 if (amt) {
381 if (amt < sbytes)
382 bzero(&s.sg_data[amt], sbytes - amt);
383 r = set_parity(s.sg_data, s.sg_badmap);
384 if (r) fprintf(stderr, "** warning: ecc problem !!\n");
385 if (ioctl(tfd, QIOWRITE, &s) < 0) {
386 perror("QIOWRITE");
387 exit(1);
388 }
389 tvsize += amt;
390 }
391 }
392
393 /* Build new header info */
394 /* ftfilt vol*xx yyyyyyyyyy note56789012345678 */
395 /* 01234567890123456789012345678901234567890123 */
396
397 sprintf(tmpstr, "ftfilt vol%s%02d %010d %s",
398 (tvlast) ? "*" : " ", tvno, tvsize, tvnote);
399 strncpy(hptr->qh_tname, tmpstr, 44);
400 UL_VAL(hptr->qh_chgdate,0) = qnow;
401
402 /* Update the header for this volume */
403 if (hsn >= 0) {
404 s.sg_trk = hsn / geo.g_segtrk;
405 s.sg_seg = hsn % geo.g_segtrk;
406 s.sg_badmap = 0;
407 s.sg_data = (UCHAR *)hbuff;
408 r = set_parity(s.sg_data, s.sg_badmap);
409 if (r) fprintf(stderr, "** warning: header ecc problem !!\n");
410 if (ioctl(tfd, QIOWRITE, &s) < 0) {
411 perror("QIOWRITE");
412 exit(1);
413 }
414 }
415 if (dhsn >= 0) {
416 s.sg_trk = dhsn / geo.g_segtrk;
417 s.sg_seg = dhsn % geo.g_segtrk;
418 s.sg_badmap = 0;
419 s.sg_data = (UCHAR *)hbuff;
420 r = set_parity(s.sg_data, s.sg_badmap);
421 if (r) fprintf(stderr, "** warning: duphdr ecc problem !!\n");
422 if (ioctl(tfd, QIOWRITE, &s) < 0) {
423 perror("QIOWRITE");
424 exit(1);
425 }
426 }
427 ioctl(tfd, QIOREWIND);
428 if (tvlast) break;
429 ask_vol(++vno);
430 }
431 return(0);
432}