#include "V.h"
#include "sys_fcntl.h"
#include "sys_stat.h"
/*
*   overwrite [N] file...
*
* Overwrite the first N bytes of the file(s) with new data.  The  data
* will consist of whatever is in buf[], repeated indefinitely.
*
* The  primary  motive  for  writing  this program was to do something
* about files that get I/O errors. Most Unix kernels don't handle this
* gracefully,  even after two decades.  In most cases, the disk sector
* will  recover  if  you  can  persuade  the  kernel  to   write   it.
* Unfortunately, Unix likes to read things before writing them, and we
* need to be tricky.  The trick that you need to  know  to  understand
* what's going on is:  If you know the kernel's block size (the larger
* size on filesystems with two block sizes ;-), and  you  do  a  write
* that  starts  exactly on a block boundary and whose size is an exact
* multiple of the block size, then the kernel's disk-strategy routines
* are  generally  smart  enough to realize that the read isn't needed.
* They will just grab the buffer(s) needed and fill in the data, which
* will get written to the file the next time a (f)sync occurs.
*
* Note  the SIZ constant.  This is the default block size that we will
* try to write. If it is not a multiple of the system block size, this
* program might not work right, because the kernel will attempt a read
* of a full block before doing the write (which is really just a  copy
* into  a  kernel  buffer).   If  SIZ  is  a  multiple of the kernel's
* (largest) block size, then the writes will be true writes,  with  no
* initial fetch of the data.
*
* It  used  to  be that you could just grab the BLKSIZ constant out of
* the /usr/include/sys headers and use that, but vendors  have  gotten
* too  clever for us, and there's no longer a reliable way that I know
* of to determine the "real" kernel block size. The 8K value used here
* works on all Unices that I know of except for Crays.
*
* AUTHOR: John Chambers
*   <jc@trillian.mit.edu>
*/
#define SIZ (8<<10)
global char buf[SIZ];
global long size = 0;
global int  files = 0;

main(ac,av)
	int    ac;
	char** av;
{	int    status=0;
	int    a, i, n;
	int    c0, c1;

	ac = Vinit(ac,av);
	for (i=1; i<=SIZ; i++)
		buf[i-1] = (i % 64) == 0 ? '\n'
		         : (i % 10) == 0 ? '0' + ((i / 10) % 10)
		         : (i %  5) == 0 ? ':'
		         : '.';

	for (a=1; a<ac; a++) {
		Switch(c0 = av[a][0]) {
		case '9': case '8': case '7':
		case '6': case '5': case '4':
		case '3': case '2': case '1':
		case '0':
			size = c0 - '0';
			for (i=1; c1=av[a][i]; i++) {
				Switch(c1) {
				case '9': case '8': case '7':
				case '6': case '5': case '4':
				case '3': case '2': case '1':
				case '0':
					size = (size * 10) + (c1 - '0');
					break;
				case 'K': case 'k':
					size <<= 10;
					break;
				case 'M': case 'm':
					size <<= 20;
					break;
				default:
					V5 "Size char %02X='%c' ignored.",B8(c1),Dsp(c1) D;
				}
			}

			V3 "Size: %d.",size D;
			break;
		default:
			++files;
			V5 "File %d: %s",files,Dsps(av[a],-1) D;
			onefile(av[a]);
		}
	}
	V3 "%d files written.",files D;
	Exit(status);
}

FCT onefile(name)
	char* name;
{	int   r=0;
	int   f, i, l, n;
	int   count=0;
	long  bytes;
	STat  st;
	Fenter("onefile");
	n = Strlen(name);
	V7 "File: %s",Dsps(name,n) D;
	if (!(bytes=size)) {
		V5 "Size unknown; get file size ..." D;
		if (Stat(name,&st)) {
			V3 "Can't stat(%s), using %d [Err %d=%s=%s]",
				name,SIZ,Errinfo D;
			st.st_size = SIZ;
		}
		bytes = st.st_size;
	}
	V5 "Write %d bytes for %s",bytes,Dsps(name,n) D;
	if ((f = Open3(name,O_WRONLY|O_CREAT,0666)) < 0) {
		V2 "Can't write %s [Err %d=%s=%s]",Dsps(name,n),Errinfo D;
		r = errno;
		Fail;
	}
	while (count < bytes) {
		l = Min(SIZ,bytes-count);
		V4 "%d bytes written; try to write %d ...",count,l D;
		if ((i = Write(f,buf,l)) < 0) {
			V2 "Can't write %d bytes to file %d=%s [Err %d=%s=%s]",
				l,f,Dsps(name,n),Errinfo D;
			r = errno;
			Fail;
		}
		count += i;
		fsync(f);
	}
	V4 "%d bytes written; done with %s.",count,Dsps(name,n) D;
	Close(f);
fail:
	FExit;
	return r;
}
