/*	clipboard version 1.0.1
 *	a simple command-line tool to get or set the clipboard
 *	usage: clipboard #prints the current scrap
 *	[some command] | clipboard #changes the scrap to whatever comes in off the pipe
 *	written by Mac-arena the Bored Zo on 19 June 2003
 *  this version released 20 June 2003 (change: added newline translation)
 *	knowledge of the existence of fcntl provided by Landon Fuller
 *	http://boredzo.fourx.org/clipboard/
 *	this program is provided AS-IS with NO WARRANTY AT ALL. any damage occurring as a result of compiling and/or using this program (including, but not limited to, clobbering of whatever happened to be on the clipboard) is entirely your own fault.
 *	this program requires Mac OS X 10.0 or later.
 */

#include <stdio.h>
#include <fcntl.h>
#include <Carbon.h>

enum {
	kbufsize = 4096 //default buffer size
};

int main(void) {
	OSStatus err = noErr;
	ScrapRef curscrap = NULL;
	ScrapFlavorFlags flags = 0;

	err = GetCurrentScrap(&curscrap);
	if(err != noErr) {
		fprintf(stderr, "GetCurrentScrap failed with error %li\n", err);
		return err;
	}

	err = GetScrapFlavorFlags(curscrap, kScrapFlavorTypeText, &flags);
	if(err != noErr) {
		fprintf(stderr, "GetScrapFlavorFlags failed with error %li\n", err);
		return err;
	}

	/*read stdin*/ {
		size_t bufsize = 0;
		size_t amtread = kbufsize;
		char *buf = malloc(kbufsize);

		fcntl(0, F_SETFL, O_NONBLOCK | O_APPEND);
		while(amtread > 0 && buf != NULL) {
			amtread = fread(&buf[bufsize], sizeof(char), kbufsize, stdin);
			/*	the way that works is fairly simple:
			 *	first run: &buf[0]
			 *	second run: &buf[kbufsize * 1]
			 *	third run: &buf[kbufsize * 2]
			 *	...
			 */
			bufsize += amtread;
			buf = realloc(buf, bufsize);
			fcntl(0, F_SETFL, O_APPEND);
		};
		//when amtread is 0, stdin is exhausted, so we're finished.

		if(bufsize == 0) {
			//note: if bufsize is 0, then realloc freed the buffer for us.
			Size scrapsize = 0;

			err = GetScrapFlavorSize(curscrap, kScrapFlavorTypeText, &scrapsize);
			if(err != noErr) {
				fprintf(stderr, "GetScrapFlavorSize failed with error %li\n", err);
				return err;
			}

			buf = malloc(scrapsize + 1);
			if(buf == NULL) {
				fprintf(stderr, "Could not allocate %u bytes for scrap contents\n", scrapsize);
				return 1;
			}

			err = GetScrapFlavorData(curscrap, kScrapFlavorTypeText, &scrapsize, buf);
			if(err != noErr) {
				fprintf(stderr, "GetScrapFlavorData failed with error %li\n", err);
				return err;
			}

			/*iterate over buf, printing chunks of non-newlines and translating newlines*/ {
				char *slicestart = NULL;
				register size_t slicelen = 0;
				register char *cur = buf;

				for(; *cur != '\0'; ++cur)
					if(*cur != '\n' && *cur != '\r') {
						if(slicestart == NULL)
							slicestart = cur;
						++slicelen;
					} else {
						fwrite(slicestart, sizeof(char), slicelen, stdout);
						slicestart = NULL;
						slicelen = 0;
						switch(*cur) {
							case '\n':
								fputc('\r', stdout);
								break;
							case '\r':
								fputc('\n', stdout);
								break;
						}
					}

				
//			fwrite(buf, sizeof(char), scrapsize, stdout);
				if(scrapsize > 0 && buf[scrapsize-1] != '\r')
					fputc('\n', stdout);
			}
		} else {
//#if 0
			err = ClearCurrentScrap();
			if(err != noErr) {
				fprintf(stderr, "ClearCurrentScrap failed with error %li\n", err);
				return err;
			}

			err = GetCurrentScrap(&curscrap);
			if(err != noErr) {
				fprintf(stderr, "GetCurrentScrap failed with error %li\n", err);
				return err;
			}
//#endif
#if 0
			err = ClearScrap(&curscrap);
			if(err != noErr) {
				fprintf(stderr, "ClearScrap failed with error %li\n", err);
				return err;
			}
#endif

			/*iterate over buf, printing chunks of non-newlines and translating newlines*/ {
				register char *cur = buf;

				for(; *cur != '\0'; ++cur)
					if(*cur == '\n')
						*cur = '\r';
					else if(*cur == '\r')
						*cur = '\n';
			}

			err = PutScrapFlavor(curscrap, kScrapFlavorTypeText, flags, bufsize, buf);
			if(err != noErr)
				fprintf(stderr, "PutScrapFlavor failed with error %li\n", err);

			free(buf);
		}
	}

	return err;
}

