From decwrl!wuarchive!cs.utexas.edu!uunet!allbery Fri Nov 3 19:53:34 PST 1989 Article 1154 of comp.sources.misc: Path: decwrl!wuarchive!cs.utexas.edu!uunet!allbery From: allbery@uunet.UU.NET (Brandon S. Allbery - comp.sources.misc) Newsgroups: comp.sources.misc Subject: v08i107: Public Domain Multi-Diskette I/O Package (Part 1 of 2) Message-ID: <71233@uunet.UU.NET> Date: 3 Nov 89 01:30:08 GMT Sender: allbery@uunet.UU.NET Reply-To: greggy@etude.UUCP (Greg Yachuk) Lines: 1336 Approved: allbery@uunet.UU.NET (Brandon S. Allbery - comp.sources.misc) Posting-number: Volume 8, Issue 107 Submitted-by: greggy@etude.UUCP (Greg Yachuk) Archive-name: mdcopy/part01 #! /bin/sh # This is a shell archive. Remove anything before this line, then unpack # it by saving it into a file and typing "sh file". To overwrite existing # files, type "sh file -c". You can also feed this as standard input via # unshar, or by typing "sh 'MANIFEST' <<'END_OF_FILE' X File Name Archive # Description X----------------------------------------------------------- X MANIFEST 1 X README 1 As much documentation as you'll get X democopy.c 1 A demo program to show usage X makefile 2 X mdclose.c 2 X mdcopy.c 1 A more sophisticated demo program X mddata.c 1 X mdfill.c 2 X mdflush.c 2 X mdgetbuf.c 2 X mdiskio.h 2 THE header file X mdopen.c 1 X mdread.c 2 X mdseek.c 2 X mdsetbuf.c 2 X mdungetc.c 2 X mdwrite.c 2 X unixcopy.c 1 A Unix specific demo program END_OF_FILE if test 793 -ne `wc -c <'MANIFEST'`; then echo shar: \"'MANIFEST'\" unpacked with wrong size! fi # end of 'MANIFEST' fi if test -f 'README' -a "${1}" != "-c" ; then echo shar: Will not clobber existing file \"'README'\" else echo shar: Extracting \"'README'\" \(14317 characters\) sed "s/^X//" >'README' <<'END_OF_FILE' XMulti-Disk I/O Package X---------------------- X XThis is an input/output package designed to allow a file to span Xmultiple diskettes. It mimics much of the functionality of the stdio Xpackage. When writing to a file, the flow of control goes something Xlike this: X X 1) A file is opened with mdopen(). Information is written to the X open file using mdputc(), mdwrite(), or both. When there is no more X room to write data onto the given file, a user callback `ateof' X function is called. If we want to continue, we will return a zero. X X 2) The file is closed, and another user callback function is called, X this time to prompt the user between diskettes. If we want to X continue, we return a 1. X X 3) It is assumed that a new diskette is now inserted. A third user X callback function `filenm()' is called, which returns the name of the X file to be opened on this diskette. X X 4) The file is opened for writing, and writing continues. X XWhen reading from a file, the flow of control goes something like this: X X 1) A file is opened with mdopen(). Information is read from the X file using mdgetc(), mdread, or both. When there is no more data X left in the file, a user callback `ateof' function is called. If X this is *really* the end of the file (i.e. this is the last diskette X we are expecting), then we return a 1, and an EOF is returned to the X caller of the mdgetc() or mdread(). Otherwise, we return a zero, X and continue from step 2 (above). X XData Structures X--------------- X Xstruct mdiskbuf { X char *bufptr; /* pointer to next position to fill */ X int bufcnt; /* number of bytes left to fill */ X char *buffer; /* buffer of data */ X char *basename; /* base name of the file */ X char *filename; /* constructed filename */ X int seq; /* next sequence number to be used */ X char fid; /* file id */ X char spare; /* in case we can't allocate a buffer */ X char flags; /* high level flags */ X int oflag; /* file creation flags */ X char *(*filenm)(); /* to construct new filename */ X int (*ateof)(); /* at *final* EOF? */ X int (*prompt)(); /* to prompt between diskettes */ X}; X#define MDFILE struct mdiskbuf X XSome of these fields are specifically for the mdiskio package, and some Xare for the user program. Specifically, the bufptr, bufcnt and buffer Xfields are to keep track of internally buffered data. X XThe `basename' is a pointer to a copy of the filename passed to Xmdopen(). Space for the copy is malloc()'d. The `filename' field is Xnormally a pointer to the same place as `basename', but may completely Xbe under user control. The only mdiskio routine which accesses this Xfield is the default (*filenm)() function, which may be overridden. XThis function is described in greater detail under `mdopen'. X XThe `seq' field is not used at all by the mdiskio package. It is Xstrictly for the user program. The supposition is that the user may Xwish to keep track of the diskette number, so a convenient place is Xprovided. X XThe `fid' field is the file id, as returned from open(2). It should not Xbe modified by the user program. X XThe `spare' field is used as a single byte buffer, if no buffer space is Xavailable. Normally the buffer space is malloc()'d. X XThe `flags' field contains buffer information, such as whether the Xbuffer is empty, whether it has been read from or written to, as well as Xwhether we are at the *real* EOF, and if an error has occured. X XThe `oflags' field contains the flags that are to be passed to each call Xto open(). Since a file must be opened on each diskette, we must keep Xtrack of how the user program wants it opened. X XThe `filenm' field points to a function which takes a pointer to an Xmdiskbuf structure as its sole parameter, and returns a pointer to the Xname of a file to be opened. The user may supply his/her own function, Xif for instance they wish to return "filename.001", "filename.002", etc. Xbased on the sequence number. The default function simply returns the X`basename' pointer. X XThe `ateof' field points to a function which takes a pointer to an Xmdiskbuf structure and an access-type integer as its parameters. It is Xcalled whenever the mdiskio package hits end-of-file, in order to ask Xthe user program if this is *really* the end-of-file, or whether there Xis another file on a different diskette to open. This function returns X1 if we are *really* at the end of the file, and 0 if there is another Xdiskette to be accessed. The default routine says that input files are Xalways at EOF, and that output files are never at EOF. This allows Xregular (non-spanning) files to be read properly. The default function Xwill be discussed in greater detail below. X XThe `prompt' field points to a function which takes a pointer to an Xmdiskbuf structure. It's function is to prompt the user to take some Xaction, such as putting a new diskette in the drive. If all has gone Xwell, it should return 1, and return a zero if something has gone wrong. X XFunctions X--------- X XMDFILE *mdopen(char *name,char *mode,char *(*fnfilenm)(),int seq); X X This function opens a named file with a given mode. The name X is any name which is valid as a parameter to `fopen'. The mode X may be one of the following strings: X X "r" Opens for reading. If the file does not exist, or X cannot be found, the `mdopen' call will fail. X X "w" Opens an empty file for writing. If the given file X exists, its contents are destroyed. X X "a" Opens for writing at the end of the file (appending). X The file is created if it does not exist. X X "r+" Opens for both reading and writing. The file must X exist. X X "w+" Opens an empty file for both reading and writing. If X the given file exists, its contents are destroyed. X X "a+" Opens for reading and appending. The file is first X created if it does not exist. X X Additionally, one of the following characters may be appended X (or inserted after the '+'): X X t Open in text (translated) mode. In this mode, CR-LF X combinations are translated into single LF's in input X and output LF's are translated to CR-LF. Note: this is X operating system dependent. It has been included for X MS/PC DOS systems. X X b Open text in binary (untranslated) mode. The above X translations are suppressed. X X The `fnfilenm' parameter points to a function which takes a pointer X to an open MDFILE and returns a filename. It is called before the X file is actually opened, so that it takes effect even for the first X segment of a file. If it is NULL, a default function is called X which simply returns a pointer to the supplied filename. The X function (either the default or the user supplied one) is stored and X called each time a new file (i.e. on a new diskette) is to be X opened. X X The `seq' parameter is saved as the initial sequence number. It is X never accessed by the mdiskio routines, but is available to the user X callback functions within the mdiskbuf structure. X X Of special note here, because I can't think of where else to put it, X is that mdopen() sets up a few default callback functions. One is X mdateof() as the `ateof' function which is described below. The X other is a default prompt function. This function writes a message X to the stderr, and waits for a carriage return or ENTER key from the X keyboard. Note that it gets the character from the keyboard, not X from stdin. X Xint mdclose(MDFILE *mdp); X X The open file pointed to by `mdp' is closed. If it is open for X output, its buffers are first flushed. After closing, any allocated X buffers are freed. There is currently no return value. X X Xint mdflush(MDFILE *mdp); X X If the file pointed to by `mdp' is open for writing, all buffered X data is written to the file. If the file is open for reading, the X the buffer is cleared. If the file is open in read/write mode (e.g. X "r+"), input operations and output operations must be separated by a X call to `mdflush' (or to `mdseek' which forces a flush). X X Xlong mdtell(MDFILE *mdp); X X This function takes an open file pointer and returns the current X position, relative to the beginning of the file ON THIS DISKETTE. X This position is the number of bytes from the beginning of the file. X It does not take into account the size of files on previous X diskettes. X X Xlong mdseek(MDFILE *mdp,long c,int orig); X X This function sets the current position within the file. If `orig' X is 0, `c' is an offset from the beginning of the file, and had X better be positive. If `orig' is 1, `c' is relative to the current X position, and may be positive or negative. If `orig' is 2, `c' is X relative to the end of the file, and usually is negative. Seeking X past the end of file is operating system dependent. X X Data is flushed from the buffer before the seek is attempted. Thus X mdseek(mdp, 0L, 1) is equivalent to mdflush(mdp). X X Note: All seeking is relative to the current diskette. You may not X seek across diskettes. X X This function returns 0 if the seek worked correctly, and non-zero X if it did not. X X Xint mdsetbuf(MDFILE *mdp, int blen); X X A buffer of size `blen' bytes is allocated for the open file. If X any I/O operations have previously been done, this may result in X lost data. If a buffer has already been allocated, a 1 is returned. X If there is not enough memory to allocate a buffer of the requested X size, a 2 is returned. In this case, a default buffer (usually 512 X or 1024 bytes, but system dependent) will still be allocated on the X first I/O operation. If all goes well, a zero is returned. X X Xint mdgetc(MDFILE *mdp); X X This function returns a character from the specified file. If the X *real* EOF has been encountered, it returns EOF. X X Note: this is implemented as a macro. X X Xint mdread(char *ptr,int size,int nitems,MDFILE *mdp); X X This function reads `nitems' of `size' bytes each from the specified X file, and copies the data into the location indicated by `ptr'. X This had better be large enough to accomodate the data. It returns X the number of items fully read. X X Note that multiple diskettes may be spanned during a single call to X this function. The appropriate callback functions are called at the X appropriate time to ensure that this is handled correctly. This X function will not return to the caller until the data has been read X (or a *real* EOF has been encountered). X X Xint mdungetc(int c,MDFILE *mdp); X X The character in `c' is pushed back into the buffer for the supplied X open file. It is not guaranteed for more than one character. It X should only be used for files opened for reading or read/writing. X X Xint mdputc(int c,MDFILE *mdp); X X This function writes a character `c' to the specified open file. It X returns the character written. X X Note: this function is actually implemented as a macro. X Xint mdwrite(char *ptr,int size,int nitems,MDFILE *mdp); X X This function writes `nitems' of `size' bytes each to the specified X file. The data is taken from the location pointed to by `ptr'. It X returns the number of items fully read. X X See the note above under mdread(). X X Xmdsetbase(MDFILE *mdp,char *name); X X This macro allows the user program to change the basename of the X file after it has been opened. This is of little use, actually, but X could possibly be used to switch between filenames when spanning X diskettes. X X Xmdsetseq(MDFILE *mdp,int n); X X This macro allows the user program to set the current sequence X number. X X Xmdfnfilenm(MDFILE *mdp,char *(*filenm)()); X X This macro allows the user program to set the filename generating X function after a file has been opened. X X Xmdfnateof(MDFILE *mdp,int (*ateof)()); X X This macro allows the user program to set the `ateof' function. X X Xmdfnprompt(MDFILE *mdp,int (*prompt)); X X This macro allows the user program to set the `prompt' function. X X Xmdfileno(MDFILE *mdp); X X This macro allows the user program to get the file number which was X returned from the call to open(2). X X Xmdseqno(MDFILE *mdp); X X This macro allows the user program to access the current sequence X number. There is actually no real reason for this to exist any X longer, since the sequence number is completely under control of the X application, now. X X Xmdeof(MDFILE *mdp); X X This macro returns non-zero if we have encountered the *real* EOF. X X Xmderror(MDFILE *mdp); X X This macro returns non-zero if an error has occurred. X X Xmdclrerr(MDFILE *mdp); X X This macro clears any I/O related error in order to allow the user X program to attempt to continue. X X Xint mdateof(MDFILE *mdp,int mode); Xint mdalleof(MDFILE *mdp,int mode); Xint mdnoteof(MDFILE *mdp,int mode); X X These are supplied functions which may be used as the `ateof' X function. mdateof() returns a zero if the file is being written and X 1 if it is being read. This allows us to process normal X (non-spanning) files as the default. X X mdalleof() always returns a 1, which means that files can never span X diskette boundaries. A little bit silly, I know, but its there for X completeness (and it is small). X X mdnoteof() always returns a 0. It is useful if you don't know how X many diskettes your file will span, but you will get a definite X indication in the input stream indicating that you have read enough. X Sound cryptic enough? For an example, say you are reading an X archive file, and you know when you get to the end because you have X a special null header. You always want it to *not* be at EOF, so X that it will automatically ask for the next diskette. When you find X your special header, your program ends, without having ever received X a *real* EOF from the I/O package. X X XThis is as much documentation as you get for this package. If you Xwish more information, have suggestions or patches, please contact Xme. X X -greg X XGreg Yachuk Informix Software Inc., Menlo Park, CA (415) 322-6457 X{uunet,pyramid}!infmx!greggy why yes, I DID choose that login myself X END_OF_FILE if test 14317 -ne `wc -c <'README'`; then echo shar: \"'README'\" unpacked with wrong size! fi # end of 'README' fi if test -f 'democopy.c' -a "${1}" != "-c" ; then echo shar: Will not clobber existing file \"'democopy.c'\" else echo shar: Extracting \"'democopy.c'\" \(4722 characters\) sed "s/^X//" >'democopy.c' <<'END_OF_FILE' X/* X * democopy copy a file to/from multiple diskettes. X * X * This is a sample program used to illustrate the multi-disk i/o X * (mdio) package. The prompt routines are used to check for the X * continuation of a file. These are guaranteed to be called X * after no more bytes can be transferred to/from a file. X * X * When writing a file, after a diskette is full, the file is closed X * and the `output' prompt routine is called. This creates an empty X * file with an extension of .TBC (to-be-continued), and asks for the X * next diskette. X * X * When reading a file, at EOF, the `at eof' function is called. X * This looks for a file with a .TBC extension, and if found returns X * FALSE since we are not really at EOF. If this file is NOT found X * then we are really at EOF, so TRUE is returned. X * X * After the input file is closed, the `input' prompt routine is X * called which asks the user to insert the next diskette for the file. X * X * Note: using an extension can be ambiguous, but this is only a X * sample program. X * X * March 1989 created by greg yachuk. placed in the public domain. X */ X#include X#include X#include X#include X#include X#include X#include "mdiskio.h" X X/* these two guys are global for debugging purposes */ XMDFILE *ip, *op; X Xmain(argc, argv) Xint argc; Xchar **argv; X{ X char buf[BUFSIZ]; X int len; X int inprompt(); X int ateof(); X int outprompt(); X X if (argc != 3) X { X puts("usage: mdcopy srcfile dstfile"); X exit(1); X } X X if ((ip = mdopen(argv[1], "rb", NULL, 0)) == NULL) X { X perror(argv[1]); X exit(2); X } X X if ((op = mdopen(argv[2], "wb", NULL, 0)) == NULL) X { X perror(argv[2]); X exit(3); X } X X /* set buffer sizes to 16K each */ X X mdsetbuf(ip, 16*1024); X mdsetbuf(op, 16*1024); X X /* override the default "prompt" routines */ X X mdfnateof(ip, ateof); X mdfnprompt(ip, inprompt); X mdfnprompt(op, outprompt); X X /* and copy away... */ X#ifdef PROFILE X PRFstart((long far *) main); X#endif X while (!mdeof(ip)) X { X len = mdread(buf, sizeof (char), sizeof buf, ip); X if (mdwrite(buf, sizeof (char), len, op) != len) X break; X } X#ifdef PROFILE X PRFstop(); X#endif X /* alternative copy... */ X X while (!mdeof(ip) && !mdeof(op)) X mdputc(mdgetc(ip), op); X X /* gotta make sure we close, cause this flushes the buffers */ X X mdclose(ip); X mdclose(op); X X exit(0); X} X X/* X * contfnm - construct the name of the `to-be-continued' marker file. X * this is constructed from the basename with an extension X * of `tbc' (to-be-continued). X */ Xchar * Xcontfnm(mdp) XMDFILE *mdp; X{ X static char contfile[256]; X char *base; X char *dotp; X X /* copy the basename */ X strcpy(contfile, mdp->basename); X X /* strip off the drive and path from the tail */ X if ((base = strrchr(contfile, '/')) == NULL) X base = contfile; X#ifdef DOS X /* this is DOS, y'know. it allows both / and \ */ X if ((dotp = strrchr(base, '\\')) == NULL) X dotp = base; X#endif X /* scan for the '.' */ X for (; *dotp && *dotp != '.'; ++dotp); X X /* and add the new extension */ X *dotp++ = '.'; X *dotp++ = 't'; X *dotp++ = 'b'; X *dotp++ = 'c'; X *dotp = '\0'; X X return (contfile); X} X X/* X * ateof - this routine is called when EOF is reached while X * reading the input file. We check if we need to X * ask for a new diskette, by checking for the existence X * of a `.tbc' (to-be-continued) file. X */ Xateof(mdp, mode) XMDFILE *mdp; Xint mode; X{ X return (access(contfnm(mdp), 00)); X} X X/* X * inprompt - this routine is called when EOF of a diskette is X * found, but there is supposed to be another diskette. X * it prompts for the next diskette and waits for the X * user to enter a carriage-return. X */ Xinprompt(mdp) XMDFILE *mdp; X{ X /* ask for another diskette, and wait for a response */ X fputs("Please insert next diskette for ", stderr); X fputs(mdp->basename, stderr); X fputs(" and press ENTER...", stderr); X X while (getchar() != '\n'); X X /* all ready to go with the next diskette */ X X return (1); X} X X/* X * outprompt - this routine is called when no more bytes can be X * written to a file. we create a `.tbc' (to-be- X * continued) file on the same diskette. Luckily, X * DOS still has room to create a file when all the X * disk space is gone. Then ask for a new diskette. X */ Xoutprompt(mdp) XMDFILE *mdp; X{ X char *contfile; X int fd; X X /* get the name of the to-be-continued file */ X contfile = contfnm(mdp); X X /* create the to-be-continued file */ X fd = open(contfile, O_CREAT|O_WRONLY, S_IREAD|S_IWRITE); X if (fd == -1) X { X perror(contfile); /* something drastic went wrong */ X return (0); /* can't continue this file */ X } X X close(fd); X X /* ask for another diskette and wait for a response */ X _mdprompt(mdp); X X /* all ready to go with next diskette */ X X return (1); X} END_OF_FILE if test 4722 -ne `wc -c <'democopy.c'`; then echo shar: \"'democopy.c'\" unpacked with wrong size! fi # end of 'democopy.c' fi if test -f 'mdcopy.c' -a "${1}" != "-c" ; then echo shar: Will not clobber existing file \"'mdcopy.c'\" else echo shar: Extracting \"'mdcopy.c'\" \(5439 characters\) sed "s/^X//" >'mdcopy.c' <<'END_OF_FILE' X/* X * mdcopy copy a file to/from multiple diskettes. X * X * This is a sample program used to illustrate the multi-disk i/o X * (mdio) package. The prompt routines are used to check for the X * continuation of a file. These are guaranteed to be called X * after no more bytes can be transferred to/from a file. X * X * When writing a file, after a diskette is full, the file is closed X * and the `output' prompt routine is called. This creates an empty X * file with an extension of .TBC (to-be-continued), and asks for the X * next diskette. X * X * When reading a file, at EOF, the `at eof' function is called. X * This looks for a file with a .TBC extension, and if found returns X * FALSE since we are not really at EOF. If this file is NOT found X * then we are really at EOF, so TRUE is returned. X * X * After the input file is closed, the `input' prompt routine is X * called which asks the user to insert the next diskette for the file. X * X * Note: using an extension can be ambiguous, but this is only a X * sample program. X * X * March 1989 created by greg yachuk. placed in the public domain. X */ X#include X#include X#include X#include X#include X#include X#include X X#include "mdiskio.h" X X/* these two guys are global for debugging purposes */ XMDFILE *ip, *op; X Xmain(argc, argv) Xint argc; Xchar **argv; X{ X char *fname; X int diridx; X struct find_t buffer; X X if (argc < 2 || argc > 3) X usage(); X X if (_dos_findfirst(argv[1], _A_NORMAL|_A_RDONLY, &buffer)) X { X fprintf(stderr, "%s: no match\n", argv[1]); X exit(1); X } X X fname = argv[1] - 1; X do X { X diridx = strcspn(fname+1, "/\\:"); X fname += diridx + 1; X } while (*fname); X X fname -= diridx; X *fname = '\0'; X X do X { X mdcopy(argv[1], buffer.name, argc == 3 ? argv[2] : buffer.name); X } X while (_dos_findnext(&buffer) == 0); X X exit(0); X} X Xmdcopy(srcdir, srcfile, dstfile) Xchar *srcdir; Xchar *srcfile; Xchar *dstfile; X{ X char buf[BUFSIZ]; X int len; X int inprompt(); X int ateof(); X int outprompt(); X X strcpy(buf, srcdir); X strcat(buf, "/"); X strcat(buf, srcfile); X X if ((ip = mdopen(buf, "rb", NULL, 0)) == NULL) X { X perror(buf); X exit(2); X } X X if ((op = mdopen(dstfile, "wb", NULL, 0)) == NULL) X { X perror(dstfile); X exit(3); X } X X /* set buffer sizes to 16K each */ X X mdsetbuf(ip, 16*1024); X mdsetbuf(op, 16*1024); X X /* override the default "prompt" routines */ X X mdfnateof(ip, ateof); X mdfnprompt(ip, inprompt); X mdfnprompt(op, outprompt); X X /* and copy away... */ X#ifdef PROFILE X PRFstart((long far *) main); X#endif X while (!mdeof(ip)) X { X len = mdread(buf, sizeof (char), sizeof buf, ip); X if (mdwrite(buf, sizeof (char), len, op) != len) X break; X } X#ifdef PROFILE X PRFstop(); X#endif X /* alternative copy... */ X X while (!mdeof(ip) && !mdeof(op)) X mdputc(mdgetc(ip), op); X X /* gotta make sure we close, cause this flushes the buffers */ X X mdclose(ip); X mdclose(op); X} X X/* X * usage - display the usage message and exit X */ Xusage() X{ X puts("usage: mdcopy srcfile dstfile"); X puts(" mdcopy srcfiles dstdir"); X exit(1); X} X X/* X * contfnm - construct the name of the `to-be-continued' marker file. X * this is constructed from the basename with an extension X * of `tbc' (to-be-continued). X */ Xchar * Xcontfnm(mdp) XMDFILE *mdp; X{ X static char contfile[256]; X char *base; X char *dotp; X X /* copy the basename */ X strcpy(contfile, mdp->basename); X X /* strip off the drive and path from the tail */ X if ((base = strrchr(contfile, '/')) == NULL) X base = contfile; X#ifdef DOS X /* this is DOS, y'know. it allows both / and \ */ X if ((dotp = strrchr(base, '\\')) == NULL) X dotp = base; X#endif X /* scan for the '.' */ X for (; *dotp && *dotp != '.'; ++dotp); X X /* and add the new extension */ X *dotp++ = '.'; X *dotp++ = 't'; X *dotp++ = 'b'; X *dotp++ = 'c'; X *dotp = '\0'; X X return (contfile); X} X X/* X * ateof - this routine is called when EOF is reached while X * reading the input file. We check if we need to X * ask for a new diskette, by checking for the existence X * of a `.tbc' (to-be-continued) file. X */ Xateof(mdp, mode) XMDFILE *mdp; Xint mode; X{ X return (access(contfnm(mdp), 00)); X} X X/* X * inprompt - this routine is called when EOF of a diskette is X * found, but there is supposed to be another diskette. X * it prompts for the next diskette and waits for the X * user to enter a carriage-return. X */ Xinprompt(mdp) XMDFILE *mdp; X{ X /* ask for another diskette, and wait for a response */ X fputs("Please insert next diskette for ", stderr); X fputs(mdp->basename, stderr); X fputs(" and press ENTER...", stderr); X X while (getchar() != '\n'); X X /* all ready to go with the next diskette */ X X return (1); X} X X/* X * outprompt - this routine is called when no more bytes can be X * written to a file. we create a `.tbc' (to-be- X * continued) file on the same diskette. Luckily, X * DOS still has room to create a file when all the X * disk space is gone. Then ask for a new diskette. X */ Xoutprompt(mdp) XMDFILE *mdp; X{ X char *contfile; X int fd; X X /* get the name of the to-be-continued file */ X contfile = contfnm(mdp); X X /* create the to-be-continued file */ X fd = open(contfile, O_CREAT|O_WRONLY, S_IREAD|S_IWRITE); X if (fd == -1) X { X perror(contfile); /* something drastic went wrong */ X return (0); /* can't continue this file */ X } X X close(fd); X X /* ask for another diskette and wait for a response */ X _mdprompt(mdp); X X /* all ready to go with next diskette */ X X return (1); X} END_OF_FILE if test 5439 -ne `wc -c <'mdcopy.c'`; then echo shar: \"'mdcopy.c'\" unpacked with wrong size! fi # end of 'mdcopy.c' fi if test -f 'mddata.c' -a "${1}" != "-c" ; then echo shar: Will not clobber existing file \"'mddata.c'\" else echo shar: Extracting \"'mddata.c'\" \(1385 characters\) sed "s/^X//" >'mddata.c' <<'END_OF_FILE' X/* X * Created 1989 by greg yachuk. Placed in the public domain. X */ X#include X#include X#include "mdiskio.h" X Xchar *_mdfilenm(); Xint _mdprompt(); X XMDFILE _mdstdin = X{ X (char *) 0x0400, /* bufptr */ X (int) 0, /* bufcnt */ X (char *) NULL, /* buffer */ X (char *) "stdin", /* basename */ X (char *) NULL, /* filename */ X (int) 0, /* seq */ X (char) 0, /* fid for stdout */ X (char) 0, /* spare */ X (char) _MDREAD, /* oflag */ X (char) O_RDONLY, /* flags */ X _mdfilenm, /* filenm() */ X mdateof, /* ateof() */ X _mdprompt, /* prompt() */ X}; X XMDFILE _mdstdout = X{ X (char *) 0x0400, /* bufptr */ X (int) 0, /* bufcnt */ X (char *) NULL, /* buffer */ X (char *) "stdout", /* basename */ X (char *) NULL, /* filename */ X (int) 0, /* seq */ X (char) 1, /* fid for stdout */ X (char) 0, /* spare */ X (char) _MDWRITE, /* oflag */ X (char) O_CREAT|O_TRUNC|O_WRONLY, /* flags */ X _mdfilenm, /* filenm() */ X mdateof, /* ateof() */ X _mdprompt, /* prompt() */ X}; X XMDFILE _mdstderr = X{ X (char *) 0x0400, /* bufptr */ X (int) 0, /* bufcnt */ X (char *) NULL, /* buffer */ X (char *) "stderr", /* basename */ X (char *) NULL, /* filename */ X (int) 0, /* seq */ X (char) 2, /* fid for stdout */ X (char) 0, /* spare */ X (char) _MDWRITE, /* oflag */ X (char) O_CREAT|O_TRUNC|O_WRONLY, /* flags */ X _mdfilenm, /* filenm() */ X mdateof, /* ateof() */ X _mdprompt, /* prompt() */ X}; END_OF_FILE if test 1385 -ne `wc -c <'mddata.c'`; then echo shar: \"'mddata.c'\" unpacked with wrong size! fi # end of 'mddata.c' fi if test -f 'mdopen.c' -a "${1}" != "-c" ; then echo shar: Will not clobber existing file \"'mdopen.c'\" else echo shar: Extracting \"'mdopen.c'\" \(3154 characters\) sed "s/^X//" >'mdopen.c' <<'END_OF_FILE' X/* X * Created 1989 by greg yachuk. Placed in the public domain. X */ X X#include X#include X#include X#include X#include X X#include "mdiskio.h" X X#ifndef THE_TTY X#ifdef MSDOS X#define THE_TTY "CON" /* device name of user's keyboard */ X#else X#define THE_TTY "/dev/tty" /* device name of user's keyboard */ X#endif X#endif X X/* X * mdopen - allocate an mdiskbuf and initialise some values in it. X * open the named file, in the requested mode. X * X * mode: "r" read only X * "w" write/truncate (create) X * "a" write/append (create) X * "r+" read/write (create) X * "w+" read/write/truncate (create) X * "a+" read/write/append (create) X * X * additionally, a "b" or "t" may be appended to specify BINARY X * or TEXT mode. Note: "ab+" <==> "a+b" X */ XMDFILE * Xmdopen(name, mode, fnfilenm, seq) Xchar *name; Xchar *mode; Xchar *(*fnfilenm)(); Xint seq; X{ X MDFILE *mdp; X char *_mdfilenm(); X int _mdprompt(); X X /* allocate the diskbuffer */ X X if ((mdp = (MDFILE *) calloc(1, sizeof (MDFILE))) == NULL) X return (NULL); X X /* default buffer size of 512 */ X X mdp->buffer = NULL; X mdp->bufptr = mdp->buffer + BUFSIZ; X mdp->basename = name; X X /* default filename-generation and prompt routines */ X X mdp->filenm = (fnfilenm != NULL) ? fnfilenm : _mdfilenm; X mdp->ateof = mdateof; X mdp->prompt = _mdprompt; X mdp->seq = seq; X X /* translate mode to bits that `open' expects */ X X mdp->flags = _MDEMPTY; X mdp->oflag = 0; X while (*mode) X { X switch (*mode++) X { X case 'r': /* read only, so far */ X mdp->oflag |= O_RDONLY; X mdp->flags |= _MDREAD; X break; X case 'w': /* write only, so far */ X mdp->oflag |= O_CREAT|O_TRUNC|O_WRONLY; X mdp->flags |= _MDWRITE; X break; X case 'a': /* append only, so far */ X mdp->oflag |= O_APPEND|O_CREAT|O_WRONLY; X mdp->flags |= _MDWRITE; X break; X case '+': /* read-write */ X mdp->oflag |= O_RDWR; X mdp->oflag &= ~(O_WRONLY|O_RDONLY); X mdp->flags |= _MDRDWR; X mdp->flags &= ~(_MDWRITE|_MDREAD); X break; X case 't': /* text mode */ X mdp->oflag |= O_TEXT; X break; X case 'b': /* binary mode */ X mdp->oflag |= O_BINARY; X break; X } X } X X /* and open up the file */ X X if (_mdopen(mdp) == -1) X { X free(mdp); X return (NULL); X } X X return (mdp); X} X X/* X * _mdfilenm - stupid default filename generator. X * just save the basename and return it. X */ Xchar * X_mdfilenm(mdp) XMDFILE *mdp; X{ X mdp->filename = mdp->basename; X return (mdp->basename); X} X X/* X * _mdprompt - stupid default prompter. just ask for next diskette. X */ X_mdprompt(mdp) XMDFILE *mdp; X{ X FILE *tty; X X fputs("Insert next diskette and press RETURN...", stderr); X X if ((tty = fopen(THE_TTY, "r")) == NULL) X { X perror(THE_TTY); X return (0); X } X X while (getc(tty) != '\n'); X fclose(tty); X return (1); X} X X/* X * md_ateof - stupid default `at eof' function. input files X * are always at EOF, and output file are never. X */ Xmdateof(mdp, mode) XMDFILE *mdp; Xint mode; X{ X return (mode == _MDREAD); X} X X/* X * mdalleof - always at EOF. X */ Xmdalleof(mdp, mode) XMDFILE *mdp; Xint mode; X{ X return (1); X} X X/* X * mdnoteof - never at EOF. X */ Xmdnoteof(mdp, mode) XMDFILE *mdp; Xint mode; X{ X return (0); X} END_OF_FILE if test 3154 -ne `wc -c <'mdopen.c'`; then echo shar: \"'mdopen.c'\" unpacked with wrong size! fi # end of 'mdopen.c' fi if test -f 'unixcopy.c' -a "${1}" != "-c" ; then echo shar: Will not clobber existing file \"'unixcopy.c'\" else echo shar: Extracting \"'unixcopy.c'\" \(3637 characters\) sed "s/^X//" >'unixcopy.c' <<'END_OF_FILE' X/* X * unixcopy split a file into multiple parts X * X * This is a sample program used to illustrate the multi-disk i/o X * (mdio) package. By setting a limit on the filesize, a large file X * can be split into multiple small files. I know, I know. This can X * already be done with split, but this is only a demonstration. It X * can also reassemble the parts into a whole. Again, this is only a X * demonstration. X * X * When writing a file, after a file is full the file is closed and X * the `output' prompt routine is called. This is completely silent. X * As the next file is opened, the `filenm' function computes a new X * filename. The initial file is named as specified. Subsequent files X * have an extension created from the sequence number (.002, .003, etc). X * X * When reading a file, at EOF, the `at eof' function is called. This X * looks for a file with same basename, and an extension of the next X * sequence number. If found, it returns FALSE since we are not really X * at EOF. If this file is NOT found then we are really at EOF, so TRUE X * is returned. X * X * Note: using an extension can be ambiguous, but this is only a X * sample program. X * X * March 1989 created by greg yachuk. placed in the public domain. X */ X#include X#include X#include X#include X#include X#include X#include "mdiskio.h" X X/* these two guys are global for debugging purposes */ XMDFILE *ip, *op; X Xmain(argc, argv) Xint argc; Xchar **argv; X{ X char buf[BUFSIZ]; X int len; X int ateof(); X char *filenm(); X int prompt(); X X if (argc != 3) X { X puts("usage: unixcopy srcfile dstfile"); X exit(1); X } X X /* filename, READ, no filename function, sequence starts at 1 */ X if ((ip = mdopen(argv[1], "r", NULL, 1)) == NULL) X { X perror(argv[1]); X exit(2); X } X X /* filename, WRITE, no filename function, sequence starts at 1 */ X if ((op = mdopen(argv[2], "w", NULL, 1)) == NULL) X { X perror(argv[2]); X exit(3); X } X X /* set buffer sizes to 16K each */ X X mdsetbuf(ip, 16*1024); X mdsetbuf(op, 16*1024); X X /* override the default "prompt" routines */ X X mdfnateof(ip, ateof); X mdfnprompt(ip, prompt); X mdfnprompt(op, prompt); X mdfnfilenm(ip, filenm); X mdfnfilenm(op, filenm); X X /* and copy away... */ X#ifdef PROFILE X PRFstart((long far *) main); X#endif X while (!mdeof(ip)) X { X len = mdread(buf, sizeof (char), sizeof buf, ip); X if (mdwrite(buf, sizeof (char), len, op) != len) X break; X } X#ifdef PROFILE X PRFstop(); X#endif X /* alternative copy... */ X X while (!mdeof(ip) && !mdeof(op)) X mdputc(mdgetc(ip), op); X X /* gotta make sure we close, cause this flushes the buffers */ X X mdclose(ip); X mdclose(op); X X exit(0); X} X X/* X * filefnm - construct the name of the next file to be opened in X * sequence. It consists of the base filename followed X * by a three character extension. X */ Xchar * Xfilenm(mdp) XMDFILE *mdp; X{ X static char filename[256]; X char *base; X X /* copy the basename */ X strcpy(filename, mdp->basename); X X /* isolate the base, without preceding path */ X if ((base = strrchr(filename, '/')) == NULL) X base = filename; X X /* remove any extension */ X if ((base = strrchr(base, '.')) == NULL) X base = filename + strlen(filename); X X sprintf(base, ".%.3d", ++mdp->seq); X return (filename); X} X X/* X * ateof - this routine is called when EOF is reached while reading X * the input file. We check for the existence of the next X * file in sequence. X */ Xateof(mdp, mode) XMDFILE *mdp; Xint mode; X{ X char *fname; X struct stat buf; X X fname = filenm(mdp); X --mdp->seq; X return (stat(fname, &buf)); X} X X/* X * prompt - completely silent. X */ Xprompt(mdp) XMDFILE *mdp; X{ X return (1); X} END_OF_FILE if test 3637 -ne `wc -c <'unixcopy.c'`; then echo shar: \"'unixcopy.c'\" unpacked with wrong size! fi # end of 'unixcopy.c' fi echo shar: End of archive 1 \(of 2\). cp /dev/null ark1isdone MISSING="" for I in 1 2 ; do if test ! -f ark${I}isdone ; then MISSING="${MISSING} ${I}" fi done if test "${MISSING}" = "" ; then echo You have unpacked both archives. rm -f ark[1-9]isdone else echo You still need to unpack the following archives: echo " " ${MISSING} fi ## End of shell archive. exit 0