2 * Amanda, The Advanced Maryland Automatic Network Disk Archiver
3 * Copyright (c) 1991-1998 University of Maryland at College Park
6 * Permission to use, copy, modify, distribute, and sell this software and its
7 * documentation for any purpose is hereby granted without fee, provided that
8 * the above copyright notice appear in all copies and that both that
9 * copyright notice and this permission notice appear in supporting
10 * documentation, and that the name of U.M. not be used in advertising or
11 * publicity pertaining to distribution of the software without specific,
12 * written prior permission. U.M. makes no representations about the
13 * suitability of this software for any purpose. It is provided "as is"
14 * without express or implied warranty.
16 * U.M. DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING ALL
17 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO EVENT SHALL U.M.
18 * BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
19 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION
20 * OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN
21 * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
23 * Author: James da Silva, Systems Design and Analysis Group
24 * Computer Science Department
25 * University of Maryland at College Park
29 * $Id: tapeio.c,v 1.53 2006/01/14 04:37:20 paddy_s Exp $
31 * implements generic tape I/O functions
39 #include "fileheader.h"
45 #include "output-tape.h"
46 #include "output-null.h"
47 #include "output-rait.h"
48 #include "output-file.h"
50 static struct virtualtape {
52 int (*xxx_tape_access) P((char *, int));
53 int (*xxx_tape_open) (char *, int, int);
54 int (*xxx_tape_stat) P((char *, struct stat *));
55 int (*xxx_tapefd_close) P((int));
56 int (*xxx_tapefd_fsf) P((int, int));
57 ssize_t (*xxx_tapefd_read) P((int, void *, size_t));
58 int (*xxx_tapefd_rewind) P((int));
59 void (*xxx_tapefd_resetofs) P((int));
60 int (*xxx_tapefd_unload) P((int));
61 int (*xxx_tapefd_status) P((int, struct am_mt_status *));
62 int (*xxx_tapefd_weof) P((int, int));
63 ssize_t (*xxx_tapefd_write) P((int, const void *, size_t));
64 int (*xxx_tapefd_can_fork) P((int));
66 /* note: "tape" has to be the first entry because it is the
67 ** default if no prefix match is found.
69 {"tape", tape_tape_access, tape_tape_open, tape_tape_stat,
70 tape_tapefd_close, tape_tapefd_fsf,
71 tape_tapefd_read, tape_tapefd_rewind, tape_tapefd_resetofs,
72 tape_tapefd_unload, tape_tapefd_status, tape_tapefd_weof,
73 tape_tapefd_write, tape_tapefd_can_fork },
74 {"null", null_tape_access, null_tape_open, null_tape_stat,
75 null_tapefd_close, null_tapefd_fsf,
76 null_tapefd_read, null_tapefd_rewind, null_tapefd_resetofs,
77 null_tapefd_unload, null_tapefd_status, null_tapefd_weof,
78 null_tapefd_write, null_tapefd_can_fork },
79 {"rait", rait_access, rait_tape_open, rait_stat,
80 rait_close, rait_tapefd_fsf,
81 rait_read, rait_tapefd_rewind, rait_tapefd_resetofs,
82 rait_tapefd_unload, rait_tapefd_status, rait_tapefd_weof,
83 rait_write, rait_tapefd_can_fork },
84 {"file", file_tape_access, file_tape_open, file_tape_stat,
85 file_tapefd_close, file_tapefd_fsf,
86 file_tapefd_read, file_tapefd_rewind, file_tapefd_resetofs,
87 file_tapefd_unload, file_tapefd_status, file_tapefd_weof,
88 file_tapefd_write, file_tapefd_can_fork },
92 static struct tape_info {
104 static int tape_info_count = 0;
106 static char *errstr = NULL;
109 * Additional initialization function for tape_info table.
116 struct tape_info *t = ptr;
125 * Convert the "name" part of a device to a vtape slot.
129 name2slot(name, ntrans)
136 if(0 != (pc = strchr(name, ':'))) {
138 for( i = 0 ; vtable[i].prefix && vtable[i].prefix[0]; i++ ) {
139 if(0 == strncmp(vtable[i].prefix, name , len)
140 && '\0' == vtable[i].prefix[len]) {
151 * Routines for parsing a device name.
155 * Initialize parsing. The text in the "dev" parameter will be altered,
156 * so a copy should be passed to us.
160 tapeio_init_devname(char * dev,
168 *dev_left = *dev_right = *dev_next = NULL; /* defensive coding */
171 * See if there is a '{' and find the matching '}'.
173 if((*dev_next = p = strchr(dev, '{')) != NULL) {
177 while((ch = *p++) != '\0' && ch != '{' && ch != '}') {}
180 * Did not find a matching '}'.
185 } else if(ch == '{') {
187 } else if(ch == '}') {
191 if(strchr(p, '{') != NULL || strchr(p, '}') != NULL) {
194 return -1; /* only one list allowed */
196 *dev_left = dev; /* text before the '{' */
197 **dev_next = '\0'; /* zap the '{' */
198 (*dev_next)++; /* point to the first name */
199 p[-1] = '\0'; /* zap the '}' */
200 *dev_right = p; /* text after the '}' */
203 * Arrange to return just one name.
206 *dev_left = *dev_right = "";
212 * Return the next device name. A dynamic area is returned that the
213 * caller is responsible for freeing.
217 tapeio_next_devname(char * dev_left,
225 p = next = *dev_next; /* remember the start point */
228 while((ch = *p++) != '\0' && ch != '{' && ch != '}' && ch != ',') {}
231 * Found the end of a name.
235 return NULL; /* end of the list */
237 p--; /* point to the null byte */
239 } else if(ch == '{') {
241 } else if(ch == '}') {
245 } while(depth != 0 || ch != ',');
247 p[-1] = '\0'; /* zap the ',' */
249 *dev_next = p; /* set up for the next call */
250 return vstralloc(dev_left, next, dev_right, NULL);
254 * The following functions get/set fields in the tape_info structure.
255 * To allow them to be called (e.g. set) from lower level open functions
256 * started by tape_open, we check and allocate the tape_info structure
257 * here as well as in tape_open.
261 tapefd_getinfo_host(fd)
264 amtable_alloc((void **)&tape_info,
270 if(tape_info[fd].master_fd != -1)
271 return tapefd_getinfo_host(tape_info[fd].master_fd);
272 return tape_info[fd].host;
276 tapefd_setinfo_host(fd, v)
280 amtable_alloc((void **)&tape_info,
286 amfree(tape_info[fd].host);
288 tape_info[fd].host = stralloc(v);
293 tapefd_getinfo_disk(fd)
296 amtable_alloc((void **)&tape_info,
302 if(tape_info[fd].master_fd != -1)
303 return tapefd_getinfo_disk(tape_info[fd].master_fd);
304 return tape_info[fd].disk;
308 tapefd_setinfo_disk(fd, v)
312 amtable_alloc((void **)&tape_info,
318 amfree(tape_info[fd].disk);
320 tape_info[fd].disk = stralloc(v);
325 tapefd_getinfo_level(fd)
328 amtable_alloc((void **)&tape_info,
334 if(tape_info[fd].master_fd != -1)
335 return tapefd_getinfo_level(tape_info[fd].master_fd);
336 return tape_info[fd].level;
340 tapefd_setinfo_level(fd, v)
344 amtable_alloc((void **)&tape_info,
350 tape_info[fd].level = v;
354 tapefd_getinfo_datestamp(fd)
357 amtable_alloc((void **)&tape_info,
363 return tape_info[fd].datestamp;
367 tapefd_setinfo_datestamp(fd, v)
371 amtable_alloc((void **)&tape_info,
377 tape_info[fd].datestamp = newstralloc(tape_info[fd].datestamp, v);
381 tapefd_getinfo_length(fd)
384 amtable_alloc((void **)&tape_info,
390 return tape_info[fd].length;
394 tapefd_setinfo_length(fd, v)
398 amtable_alloc((void **)&tape_info,
404 tape_info[fd].length = v;
408 tapefd_getinfo_tapetype(fd)
411 amtable_alloc((void **)&tape_info,
417 return tape_info[fd].tapetype;
421 tapefd_setinfo_tapetype(fd, v)
425 amtable_alloc((void **)&tape_info,
431 tape_info[fd].tapetype = newstralloc(tape_info[fd].tapetype, v);
435 tapefd_getinfo_fake_label(fd)
438 amtable_alloc((void **)&tape_info,
444 return tape_info[fd].fake_label;
448 tapefd_setinfo_fake_label(fd, v)
452 amtable_alloc((void **)&tape_info,
458 tape_info[fd].fake_label = v;
462 tapefd_getinfo_ioctl_fork(fd)
465 amtable_alloc((void **)&tape_info,
471 return tape_info[fd].ioctl_fork;
475 tapefd_setinfo_ioctl_fork(fd, v)
479 amtable_alloc((void **)&tape_info,
485 tape_info[fd].ioctl_fork = v;
489 tapefd_set_master_fd(fd, master_fd)
493 amtable_alloc((void **)&tape_info,
499 tape_info[fd].master_fd = master_fd;
504 * The normal tape operation functions.
508 tape_access(filename, mode)
515 vslot = name2slot(filename, &tname);
516 return vtable[vslot].xxx_tape_access(tname, mode);
520 tape_stat(filename, buf)
527 vslot = name2slot(filename, &tname);
528 return vtable[vslot].xxx_tape_stat(tname, buf);
532 tape_open(char *filename, int mode, ...)
541 mask = va_arg(ap, int);
544 vslot = name2slot(filename, &tname);
545 if((fd = vtable[vslot].xxx_tape_open(tname, mode, mask)) >= 0) {
546 amtable_alloc((void **)&tape_info,
553 * It is possible to recurse in the above open call and come
554 * back here twice for the same file descriptor. Set the vtape
555 * index only if it is not already set, i.e. the first call wins.
557 if(tape_info[fd].vtape_index < 0) {
558 tape_info[fd].vtape_index = vslot;
571 || fd >= tape_info_count
572 || (vslot = tape_info[fd].vtape_index) < 0) {
576 if((res = vtable[vslot].xxx_tapefd_close(fd)) == 0) {
577 amfree(tape_info[fd].host);
578 amfree(tape_info[fd].disk);
579 amfree(tape_info[fd].datestamp);
580 amfree(tape_info[fd].tapetype);
581 memset(tape_info + fd, 0, sizeof(*tape_info));
582 tape_info_init((void *)(tape_info + fd));
594 || fd >= tape_info_count
595 || (vslot = tape_info[fd].vtape_index) < 0) {
599 res = vtable[vslot].xxx_tapefd_can_fork(fd);
605 tapefd_fsf(fd, count)
612 || fd >= tape_info_count
613 || (vslot = tape_info[fd].vtape_index) < 0) {
617 return vtable[vslot].xxx_tapefd_fsf(fd, count);
627 || fd >= tape_info_count
628 || (vslot = tape_info[fd].vtape_index) < 0) {
632 return vtable[vslot].xxx_tapefd_rewind(fd);
642 || fd >= tape_info_count
643 || (vslot = tape_info[fd].vtape_index) < 0) {
644 errno = EBADF; /* not that it matters */
647 vtable[vslot].xxx_tapefd_resetofs(fd);
657 || fd >= tape_info_count
658 || (vslot = tape_info[fd].vtape_index) < 0) {
662 return vtable[vslot].xxx_tapefd_unload(fd);
666 tapefd_status(fd, stat)
668 struct am_mt_status *stat;
673 || fd >= tape_info_count
674 || (vslot = tape_info[fd].vtape_index) < 0) {
678 return vtable[vslot].xxx_tapefd_status(fd, stat);
682 tapefd_weof(fd, count)
689 || fd >= tape_info_count
690 || (vslot = tape_info[fd].vtape_index) < 0) {
694 return vtable[vslot].xxx_tapefd_weof(fd, count);
698 tapefd_read(fd, buffer, count)
706 || fd >= tape_info_count
707 || (vslot = tape_info[fd].vtape_index) < 0) {
711 return vtable[vslot].xxx_tapefd_read(fd, buffer, count);
715 tapefd_write(fd, buffer, count)
723 || fd >= tape_info_count
724 || (vslot = tape_info[fd].vtape_index) < 0) {
728 return vtable[vslot].xxx_tapefd_write(fd, buffer, count);
738 if((fd = tape_open(devname, O_RDONLY)) < 0) {
739 r = errstr = newvstralloc(errstr,
740 "tape_rewind: tape open: ",
745 } else if(tapefd_rewind(fd) == -1) {
746 r = errstr = newvstralloc(errstr,
747 "tape_rewind: rewinding tape: ",
766 if((fd = tape_open(devname, O_RDONLY)) < 0) {
767 r = errstr = newvstralloc(errstr,
768 "tape_unload: tape open: ",
773 } else if(tapefd_unload(fd) == -1) {
774 r = errstr = newvstralloc(errstr,
775 "tape_unload: unloading tape: ",
788 tape_fsf(devname, count)
793 char count_str[NUM_STR_SIZE];
796 if((fd = tape_open(devname, O_RDONLY)) < 0) {
797 r = errstr = newvstralloc(errstr,
798 "tape_fsf: tape open: ",
803 } else if(tapefd_fsf(fd, count) == -1) {
804 snprintf(count_str, sizeof(count_str), "%d", count);
805 r = errstr = newvstralloc(errstr,
808 "file", (count == 1) ? "" : "s",
819 /* Reads the tape label, like you expect. If failure, returns an error
820 string. If the tape might not be an Amanda tape, the returned
821 string will start with NOT_AMANDA_TAPE_MSG. */
824 tapefd_rdlabel(fd, datestamp, label)
837 buflen = MAX_TAPE_BLOCK_BYTES;
838 buffer = alloc(buflen + 1);
840 if(tapefd_getinfo_fake_label(fd)) {
841 *datestamp = stralloc("X");
842 *label = stralloc(FAKE_LABEL);
843 } else if(tapefd_rewind(fd) == -1) {
844 r = stralloc2("rewinding tape: ", strerror(errno));
845 } else if((rc = tapefd_read(fd, buffer, buflen)) == -1) {
846 r = vstralloc(NOT_AMANDA_TAPE_MSG, " (",
847 strerror(errno), ")", NULL);
848 } else if (rc == 0) {
849 r = stralloc2(NOT_AMANDA_TAPE_MSG, " (Read 0 bytes)");
851 /* make sure buffer is null-terminated */
854 parse_file_header(buffer, &file, rc);
855 if(file.type != F_TAPESTART) {
856 r = stralloc(NOT_AMANDA_TAPE_MSG);
858 *datestamp = stralloc(file.datestamp);
859 *label = stralloc(file.name);
863 errstr = newvstralloc(errstr, r, NULL);
868 tape_rdlabel(devname, datestamp, label)
876 if((fd = tape_open(devname, O_RDONLY)) < 0) {
877 r = vstralloc("tape_rdlabel: tape open: ",
883 r = tapefd_rdlabel(fd, datestamp, label);
888 errstr = newvstralloc(errstr, r, NULL);
893 tapefd_wrlabel(fd, datestamp, label, size)
904 if(tapefd_rewind(fd) == -1) {
905 r = errstr = newstralloc2(errstr, "rewinding tape: ", strerror(errno));
908 file.type = F_TAPESTART;
909 strncpy(file.datestamp, datestamp, sizeof(file.datestamp) - 1);
910 file.datestamp[sizeof(file.datestamp) - 1] = '\0';
911 strncpy(file.name, label, sizeof(file.name) - 1);
912 file.name[sizeof(file.name) - 1] = '\0';
913 buffer = alloc(size);
914 file.blocksize = size;
915 build_header(buffer, &file, size);
916 tapefd_setinfo_host(fd, NULL);
917 tapefd_setinfo_disk(fd, label);
918 tapefd_setinfo_level(fd, -1);
919 if((rc = tapefd_write(fd, buffer, size)) != size) {
920 r = errstr = newstralloc2(errstr,
922 (rc != -1) ? "short write"
931 tape_wrlabel(devname, datestamp, label, size)
940 if((fd = tape_open(devname, O_WRONLY)) < 0) {
941 r = errstr = newstralloc2(errstr,
943 (errno == EACCES) ? "tape is write-protected"
945 } else if(tapefd_wrlabel(fd, datestamp, label, size) != NULL) {
955 tapefd_wrendmark(fd, datestamp, size)
966 file.type = F_TAPEEND;
967 strncpy(file.datestamp, datestamp, sizeof(file.datestamp) - 1);
968 file.datestamp[sizeof(file.datestamp) - 1] = '\0';
969 buffer = alloc(size);
970 file.blocksize = size;
971 build_header(buffer, &file, size);
972 tapefd_setinfo_host(fd, NULL);
973 tapefd_setinfo_disk(fd, "TAPEEND");
974 tapefd_setinfo_level(fd, -1);
976 if((rc = tapefd_write(fd, buffer, size)) != size) {
977 r = errstr = newstralloc2(errstr, "writing endmark: ",
978 (rc != -1) ? "short write" : strerror(errno));
986 tape_wrendmark(devname, datestamp, size)
994 if((fd = tape_open(devname, O_WRONLY)) < 0) {
995 r = errstr = newstralloc2(errstr,
997 (errno == EACCES) ? "tape is write-protected"
999 } else if(tapefd_wrendmark(fd, datestamp, size) != NULL) {
1009 tape_writable(devname)
1015 /* first, make sure the file exists and the permissions are right */
1017 if(tape_access(devname, R_OK|W_OK) == -1) {
1018 r = errstr = newstralloc(errstr, strerror(errno));
1019 } else if((fd = tape_open(devname, O_WRONLY)) < 0) {
1020 r = errstr = newstralloc(errstr,
1021 (errno == EACCES) ? "tape write-protected"
1033 * The following test program may be used to exercise I/O patterns through
1034 * the tapeio interface. Commands may either be on the command line or
1035 * read from stdin (e.g. for a test suite).
1041 /* If the C library does not define random(), try to use rand() by
1042 defining USE_RAND, but then make sure you are not using hardware
1043 compression, because the low-order bits of rand() may not be that
1045 #define random() rand()
1046 #define srandom(seed) srand(seed)
1054 fprintf(stderr, " ?|help\n");
1055 fprintf(stderr, " open [\"file\"|$TAPE [\"mode\":O_RDONLY]]\n");
1056 fprintf(stderr, " read [\"records\":\"all\"]\n");
1057 fprintf(stderr, " write [\"records\":1] [\"file#\":\"+\"] [\"record#\":\"+\"] [\"host\"] [\"disk\"] [\"level\"]\n");
1058 fprintf(stderr, " eof|weof [\"count\":1]\n");
1059 fprintf(stderr, " fsf [\"count\":1]\n");
1060 fprintf(stderr, " rewind\n");
1061 fprintf(stderr, " unload\n");
1067 fprintf(stderr, "usage: %s [-c cmd [args] [%% cmd [args] ...]]\n", pgm);
1071 #define TEST_BLOCKSIZE (32 * 1024)
1073 #define MAX_TOKENS 10
1077 static char *token_area[MAX_TOKENS + 1];
1078 static char **token;
1079 static int token_count;
1082 static int current_file = 0;
1083 static int current_record = 0;
1085 static int have_length = 0;
1086 static int length = 0;
1088 static int show_timestamp = 0;
1090 char write_buf[TEST_BLOCKSIZE];
1099 || (token_count >= 2 && strcmp(token[1], "$TAPE") == 0)) {
1100 if((file = getenv("TAPE")) == NULL) {
1101 fprintf(stderr, "tape_open: no file name and $TAPE not set\n");
1107 if(token_count > 2) {
1108 mode = atoi(token[2]);
1113 fprintf(stderr, "tapefd_open(\"%s\", %d): ", file, mode);
1114 if((fd = tape_open(file, mode, 0644)) < 0) {
1117 fprintf(stderr, "%d (OK)\n", fd);
1119 tapefd_setinfo_length(fd, length);
1129 fprintf(stderr, "tapefd_close(): ");
1130 if((result = tapefd_close(fd)) < 0) {
1133 fprintf(stderr, "%d (OK)\n", result);
1143 char buf[sizeof(write_buf)];
1150 if(token_count > 1 && strcmp(token[1], "all") != 0) {
1151 count = atoi(token[1]);
1156 for(i = 0; (! have_count) || (i < count); i++) {
1157 fprintf(stderr, "tapefd_read(%d): ", i);
1158 if((result = tapefd_read(fd, buf, sizeof(buf))) < 0) {
1161 } else if(result == 0) {
1162 fprintf(stderr, "%d (EOF)\n", result);
1164 * If we were not given a count, EOF breaks the loop, otherwise
1165 * we keep trying (to test read after EOF handling).
1171 if(result == sizeof(buf)) {
1178 * If the amount read is really short, we may refer to junk
1179 * when displaying the record data, but things are pretty
1180 * well screwed up at this point anyway so it is not worth
1181 * the effort to deal with.
1184 "%d (%s): file %d: record %d",
1189 if(show_timestamp) {
1191 tm = localtime(&then);
1193 ": %04d/%02d/%02d %02d:%02d:%02d\n",
1201 fputc('\n', stderr);
1217 if(token_count > 1) {
1218 count = atoi(token[1]);
1223 if(token_count > 2 && strcmp(token[2], "+") != 0) {
1224 current_file = atoi(token[2]);
1227 if(token_count > 3 && strcmp(token[3], "+") != 0) {
1228 current_record = atoi(token[3]);
1231 if(token_count > 4 && token[4][0] != '\0') {
1232 tapefd_setinfo_host(fd, token[4]);
1235 if(token_count > 5 && token[5][0] != '\0') {
1236 tapefd_setinfo_disk(fd, token[5]);
1239 if(token_count > 6 && token[6][0] != '\0') {
1240 tapefd_setinfo_level(fd, atoi(token[6]));
1243 p = (int *)write_buf;
1246 tm = localtime(&now);
1247 for(i = 0; i < count; i++, current_record++) {
1248 p[0] = current_file;
1249 p[1] = current_record;
1250 fprintf(stderr, "tapefd_write(%d): ", i);
1251 if((result = tapefd_write(fd, write_buf, sizeof(write_buf))) < 0) {
1255 if(result == sizeof(write_buf)) {
1261 "%d (%s): file %d: record %d",
1266 if(show_timestamp) {
1268 ": %04d/%02d/%02d %02d:%02d:%02d\n",
1276 fputc('\n', stderr);
1287 if(token_count > 1) {
1288 count = atoi(token[1]);
1293 fprintf(stderr, "tapefd_fsf(%d): ", count);
1294 if((result = tapefd_fsf(fd, count)) < 0) {
1297 fprintf(stderr, "%d (OK)\n", result);
1298 current_file += count;
1309 if(token_count > 1) {
1310 count = atoi(token[1]);
1315 fprintf(stderr, "tapefd_weof(%d): ", count);
1316 if((result = tapefd_weof(fd, count)) < 0) {
1319 fprintf(stderr, "%d (OK)\n", result);
1320 current_file += count;
1330 fprintf(stderr, "tapefd_rewind(): ");
1331 if((result = tapefd_rewind(fd)) < 0) {
1334 fprintf(stderr, "%d (OK)\n", result);
1345 fprintf(stderr, "tapefd_unload(): ");
1346 if((result = tapefd_unload(fd)) < 0) {
1349 fprintf(stderr, "%d (OK)\n", result);
1351 current_record = -1;
1360 { "?", 0, do_help },
1361 { "help", 0, do_help },
1362 { "eof", 0, do_weof },
1363 { "weof", 0, do_weof },
1364 { "fsf", 0, do_fsf },
1365 { "rewind", 0, do_rewind },
1366 { "offline", 0, do_unload },
1367 { "open", 0, do_open },
1368 { "close", 0, do_close },
1369 { "read", 0, do_read },
1370 { "write", 0, do_write },
1387 /* Don't die when child closes pipe */
1388 signal(SIGPIPE, SIG_IGN);
1390 if((pgm = strrchr(argv[0], '/')) != NULL) {
1397 * Compute the minimum abbreviation for each command.
1399 for(i = 0; cmd[i].name; i++) {
1400 cmd[i].min_chars = 1;
1402 for(j = 0; cmd[j].name; j++) {
1406 if(0 == strncmp(cmd[i].name, cmd[j].name, cmd[i].min_chars)) {
1410 if(0 == cmd[j].name) {
1418 * Process the command line flags.
1420 while((ch = getopt(argc, argv, "hcl:t")) != EOF) {
1427 length = atoi(optarg);
1430 switch(optarg[j-1] ) {
1432 case 'b': length /= 2; break;
1433 case 'M': length *= 1024; break;
1434 default: length /= 1024; break;
1451 * Initialize the write buffer.
1455 for(j = 0; j < sizeof(write_buf); j++) {
1456 write_buf[j] = (char)random();
1462 token = token_area + 1;
1463 token_area[0] = ""; /* if cmdline */
1466 for(token_count = 1;
1467 token_count < (sizeof(token_area) / sizeof(token_area[0]))
1469 token_count++, optind++) {
1470 if(strcmp(argv[optind], "%") == 0) {
1474 token_area[token_count] = argv[optind];
1477 if(token_count == 0 && optind >= argc) {
1481 if((line = areads(0)) == NULL) {
1484 if((s = strchr(line, '#')) != NULL) {
1487 s = line + strlen(line) - 1;
1488 while(s >= line && isspace(*s)) {
1491 token_count = split(line,
1493 sizeof(token_area) / sizeof(token_area[0]),
1499 * Truncate tokens at first comment indicator, then test for
1502 for(i = 0; i < token_count; i++) {
1503 if(token[i][0] == '#') {
1508 if(token_count <= 0) {
1509 continue; /* blank/comment input line */
1513 * Find the command to run, the do it.
1515 j = strlen(token[0]);
1516 for(i = 0; cmd[i].name; i++) {
1517 if(strncmp(cmd[i].name, token[0], j) == 0
1518 && j >= cmd[i].min_chars) {
1522 if(cmd[i].name == NULL) {
1523 fprintf(stderr, "%s: unknown command: %s\n", pgm, token[0]);