/*	File:		unshar.c	Contains:	unshar for MPW that's good enough for				comp.sources.unix archives	Written by:	sw				Network Analysis Ltd				178 Wainbody Ave South				Coventry CV3 6BX				UK	Phone:		+44 203 419996	E-mail:		sw@network-analysis-ltd.co.uk		Copyright:	Public domain	Change History (most recent first):	<9>			18/11/91	sw		Changes to compile with TC 5.0.	<8>			18/11/91	sw		Deal with embedded "cd"s	<7>			 19/5/91	sw		Remove tests for "if"s and make less sensitive to									variations in shar file formats. Also stop									looking for #! /bin/sh; any comment line will do.									Merge MPW and Think C vers into 1 src file.	<6>			 16/5/91	sw		Handle shar files in sumex archives.	<5>			  3/5/91	sw		Previous mod didn't get the last char of the									filename.	<4>			  3/5/91	sw		Cope with shar files that do not quote the									filename in the "if test -f" line.	<3>			  3/2/91	sw		Pick up terminating string from "sed" or "cat"									command line.	<2>			  5/7/90  	sw		Reconvert to MPW tool	<1>			  ??????	aw		Original by Amanda Walker, Intercon	To Do:*/#ifdef	MPW#include <Types.h>#include <StdDef.h>#include <Files.h>#include <CursorCtl.h>#include <StdLib.h>#endif	MPW#include <errno.h>#include <stdio.h>#include <String.h>#ifndef NIL#define NIL	(0L)#endif/* Strings used in error messages */#ifdef	MPW#define	CPSTR		c2pstr#define PCSTR		p2cstr#else#define CPSTR		CtoPstr#define PCSTR		PtoCstr#endif#ifndef EOF#define EOF	(-1L)#endif#ifndef	MPW/*	for Think C*//* if using new Think C headers, uncomment the following line, otherwise   use the line after that. Only one of the following pair of lines should be used*/#define	LOWMEM_LONG(lowMemVar)	(*((long *)lowMemVar))		/* use this if new-style TC headers *//*#define LOWMEM_LONG(lowMemVar)	(lowMemVar)					/* use this if old-style TC headers */#define DIRECTORY(pb)	(((pb).dirInfo.ioFlAttrib & 0x10) == 0x10)#define	getDir	11			/* buttons in the dialogue box */#define	getCurDir	12#define	GD_PROMPT	13typedef enum{	infoDlgRes = 1000,	aboutAlrtRes,			/* About... alert resource number */	abortRes,	gdDlgRes,	typeDlgRes,	errorAlertRes,	dupFNAlertRes};typedef enum				/* File menu item numbers */{	extract = 1,	close,	quit};typedef enum 				/* Edit menu item numbers */{	undo = 1,	/* --- */	cut = 3,	copy,	paste,	clear};typedef enum				/* Option menu item numbers */{	forceOpt = 1,	setCr};typedef enum				/* dialog item numbers */{	okB = 1,	crText,	cancelB};typedef enum{	continueB = 1,	quitB,	messageItem};MenuHandle	fileM, editM, optM;short		dirVRefNum = 0;char		*progname;short		appFileCount,			whatToDo;DialogPtr	infoDlgPtr, typeDlgPtr;#endif								/* Think C declarations */Boolean force = 	false;  		/* force overwriting existing files */OSType	fdCreator =	'MPS ';			/* Finder Creator */OSType	fdType =	'TEXT';			/* Finder Type */void ErrMsg (char *p1, char *p2);									/* for display errors */void ConvertFName (char *unixfilename, char *mpwfilename);									/* for converting Unix->Mac filenames */void unshar(char *s);				/* the guts of the program */#ifndef	MPW/*	Prototypes for Think C standalone version */void SetDText(DialogPtr dlog, int item, Str255 str);void GetDText(DialogPtr dlog, int item, StringPtr str);void GetCreator(void);void DoFileMenu(int item);void DoEditMenu(int item);void DoOptMenu(int item);void DoAbout(void);pascal short DirSelHook(int item, DialogPtr theDialog);pascal Boolean DirFilterProc(CInfoPBPtr pb);long GetDir(char *text);void GetNextFile(SFReply *fInfoPtr);void Extract(void);void SetDText(DialogPtr dlog, int item, Str255 str){Handle	itemHandle;short	itemType;Rect	itemRect;	GetDItem (dlog, item, &itemType, &itemHandle, &itemRect);	SetIText (itemHandle, str);}/* Dialog handler */void GetDText(DialogPtr dlog, int item, StringPtr str){Handle	itemHandle;short	itemType;Rect	itemRect;	GetDItem (dlog, item, &itemType, &itemHandle, &itemRect);	GetIText (itemHandle, str);}void GetCreator(void){	short	itemHit;	char	creator[5];	typeDlgPtr = GetNewDialog (typeDlgRes, NIL, (WindowPtr) -1L);	BlockMove(&fdCreator, creator+1, 4);	creator[0] = '\004';	SetDText(typeDlgPtr, crText, (StringPtr)creator);	SelectWindow(typeDlgPtr);	ShowWindow(typeDlgPtr);	DrawDialog(typeDlgPtr);	ModalDialog(NIL, &itemHit);	if (itemHit == okB) {		GetDText (typeDlgPtr, crText,(StringPtr) &creator);		BlockMove(creator+1, &fdCreator, 4);	}	DisposDialog(typeDlgPtr);}/*	File menu handler*/void DoFileMenu(int item){WindowPeek	wPeek;	switch (item)	{		case extract:			Extract();			break;		case close:			if ((wPeek = (WindowPeek) FrontWindow ()) != NIL)			{				if (wPeek->windowKind < 0)					CloseDeskAcc (wPeek->windowKind);			}			break;		case quit:			SkelWhoa ();			break;	}}void DoEditMenu(int item){DialogPtr	theDialog;	theDialog = (DialogPtr) FrontWindow ();	if (((WindowPeek) theDialog)->windowKind != dialogKind)		return;	switch (item)	{		case cut:		{			DlgCut (theDialog);			(void) ZeroScrap ();			(void) TEToScrap ();			break;		}		case copy:		{			DlgCopy (theDialog);			(void) ZeroScrap ();			(void) TEToScrap ();			break;		}		case paste:		{			(void) TEFromScrap ();			DlgPaste (theDialog);			break;		}		case clear:		{			DlgDelete (theDialog);			break;		}	}}void DoOptMenu(item)int		item;{	switch (item)	{		case forceOpt:	force = !force;										CheckItem(optM, forceOpt, force);										break;	 	case setCr:		GetCreator();	 								break;	 }}/*	Handle selection of AboutÉ item from Apple menu*/void DoAbout(void){	(void) Alert (aboutAlrtRes, NIL);}Boolean		useCurDir;		/* Set if current dir to be used *//* Filter procs & dialogue hooks to select directories only in SFGet file */pascal short DirSelHook(int item, DialogPtr theDialog){	if (item == getDir|| item == getCurDir) {		/* folder selected */		useCurDir = item == getCurDir;		item = getOpen;	}	return(item);}pascal Boolean DirFilterProc(CInfoPBPtr pb){	return(!DIRECTORY(*pb));	/* a directory if bit 4 is set */}/* * GetDir - manage the directory selection dialog */long GetDir(char *text){	Point		where;	short		ht, wd;	SFReply		reply = {0};	CInfoPBRec	pb;	DialogPtr	dlgP;	long		dirDirID=0;		/* Selected directory DirID */	if ((dlgP = GetNewDialog(gdDlgRes, NIL,(WindowPtr) -1)) == NIL) {		return(TRUE);	}	wd = (dlgP->portRect.right)-(dlgP->portRect.left);	ht = (dlgP->portRect.bottom)-(dlgP->portRect.top);	/* centre the dialogue box on the screen	   (but how do I know which screen?)	*/	where.h = (screenBits.bounds.right-screenBits.bounds.left-wd) / 2;	where.v = (screenBits.bounds.bottom-screenBits.bounds.top-ht) / 2;	ParamText((StringPtr)text, (StringPtr)"\p", (StringPtr)"\p", (StringPtr)"\p");	SFPGetFile (where,				(StringPtr)text,				(FileFilterProcPtr) DirFilterProc,				-1, NIL,				(DlgHookProcPtr) DirSelHook,				&reply,				gdDlgRes, NIL);	if (reply.good) {		dirVRefNum = reply.vRefNum;		if (useCurDir) {			dirDirID = *((long *)CurDirStore);		}		else {			dirDirID = (long)(reply.fType);		}	}	return(dirDirID);}void GetNextFile(SFReply *fInfoPtr){	AppFile	nextFile;	static	short	idx=1;	GetAppFiles(idx++, &nextFile);	fInfoPtr->vRefNum = nextFile.vRefNum;	fInfoPtr->fType = nextFile.fType;	fInfoPtr->version = nextFile.versNum;	BlockMove(nextFile.fName, fInfoPtr->fName, (int)(nextFile.fName[0]+1));	appFileCount--;}void Extract(void){	Point		where;	SFReply		reply;	while (1){		if (appFileCount > 0) GetNextFile(&reply);		else {			/*			 * Use the standard file dialog to select the archive.			 */			where.h = where.v = 75;			SFGetFile(where, (StringPtr)"\pSelect shar file", NIL, -1, NIL, NIL, &reply);			if (!reply.good)				return;		}			/*		 * Remember the VRefNum and Name for OpenArchive.		 * Find out where to put the extracted files.		 */		(void) SetVol(NIL, reply.vRefNum);		PtoCstr(reply.fName);		unshar((char *)reply.fName);		(void) SetVol(NIL, reply.vRefNum);	}}main(int argc, char *argv[]){	Handle		fTypeH;	DialogTHndl	dlgH;	Point		where;	short		ht, wd;			SkelInit (3, NIL);	SkelApple ("\pAbout UnsharÉ", DoAbout);	fileM = NewMenu (1000, (StringPtr)"\pFile");	AppendMenu (fileM, (StringPtr)"\pExtract/O;Close/K;Quit/Q");	SkelMenu (fileM, DoFileMenu, NIL, FALSE);	editM = NewMenu (1001, (StringPtr)"\pEdit");	AppendMenu (editM, (StringPtr)"\p(Undo/Z;(-;Cut/X;Copy/C;Paste/V;Clear");	SkelMenu (editM, DoEditMenu, NIL, FALSE);	optM = NewMenu (1002, (StringPtr)"\pOptions");	AppendMenu (optM, (StringPtr)"\pOverwrite existing files;File type...");	SkelMenu (optM, DoOptMenu, NIL, TRUE);		CountAppFiles (&whatToDo, &appFileCount);	if ((fTypeH = (char **)GetResource('ftyp', 0)) == NIL) {		ExitToShell();	}	BlockMove(*fTypeH, &fdCreator, 4);	BlockMove((*fTypeH)+4, &fdType, 4);	ReleaseResource(fTypeH);		CouldDialog(typeDlgRes);	CouldDialog(infoDlgRes);	dlgH = (DialogTHndl)GetResource('DLOG', typeDlgRes);	wd = ((* dlgH)->boundsRect.right)-((* dlgH)->boundsRect.left);	/* centre the dialogue box on the screen	   (but how do I know which screen?)	*/	where.h = (screenBits.bounds.right-screenBits.bounds.left-wd) / 2;	(* dlgH)->boundsRect.right += where.h;	(* dlgH)->boundsRect.left = where.h;		dlgH = (DialogTHndl)GetResource('DLOG', infoDlgRes);	wd = ((* dlgH)->boundsRect.right)-((* dlgH)->boundsRect.left);	/* centre the dialogue box on the screen	   (but how do I know which screen?)	*/	where.h = (screenBits.bounds.right-screenBits.bounds.left-wd) / 2;	(* dlgH)->boundsRect.right += where.h;	(* dlgH)->boundsRect.left = where.h;	/* if launched with a set of files, extract them first before asking	   the user for more	*/	if (appFileCount > 0)		Extract();	SkelMain ();	SkelClobber ();	FreeDialog(typeDlgRes);	FreeDialog(infoDlgRes);}#else								/* MPW main support routines */main(int argc, char *argv[]){	char	**filelist;				/* list of files to process */	int		fileCount = 0;			/* no of files to process */	#ifdef	MPW	InitCursorCtl(nil);#endif	MPW	argc--; argv++;	if ((filelist = (char **)calloc((size_t)argc, sizeof(Ptr))) == NULL) {		fprintf(stderr, "### Not enough memory\n");		exit(-1);	}	while (argc) {		if (argv[0][0] == '-') {			switch (argv[0][1]) {			case '\0':				filelist[fileCount++] = "-";				break;			case 'c':				/* creator is in next arg */				argc--;				argv++;				strncpy((char *)&fdCreator, argv[0], (size_t)4);				break;			case 'f':				force = true;				break;			case 't':				/* type is in next arg */				argc--;				argv++;				strncpy((char *)&fdType, argv[0], (size_t)4);				break;			default:				fprintf(stderr, "### Usage: unshar -f -c creator -t type [files|-]\n");				exit(-1);			}		}		else			filelist[fileCount++] = argv[0];		argc--; argv++;	}	/* at this point all files to process are in filelist */	for (argc = 0; argc < fileCount; argc++) {		unshar(filelist[argc]);	}}#endif/*	This part (mostly) common to both MPW and Think C */void ErrMsg (char *p1, char *p2){#ifdef	MPW	fprintf (stderr, "### %P %P\n", p1, p2);#else    ParamText((StringPtr)p1, (StringPtr)p2, (StringPtr)"\p", (StringPtr)"\p");    (void)StopAlert(errorAlertRes, NIL);#endif}void ConvertFName (char *unixfilename, char *macfilename){	char	*cp, *tp;	char	buf[256];	Boolean	slashSeen = false;		/* make a Mac relative pathname */	/*	rules of the game:		'foo' -> :foo		"foo" -> :foo		`foo` -> :foo		./foo -> :foo		foo/baz -> :foo:baz		.foo -> :_foo		.. -> ::		foo/./baz -> :foo:baz		/foo/baz -> :foo:baz       because we don't really want to create files at top vol level		foo:baz -> :foo/baz		rules apply recursively	*/		(void) strcpy(macfilename, ":");			/* initialize with a leading colon */	for (cp = buf, tp = unixfilename; *tp; tp++) {		switch (*tp) {			case '\'':			case '"':			case '`':	break;					/* delete these chars */			case '.':	if (*(tp+1) == '/') {	/* delete any occurence of ./ */							tp++;						}						else if (*(tp+1) == '.') {							*cp++ = ':';							*cp++ = ':';		/* convert .. to :: */							tp++;						}						else if (cp == buf)							*cp++ = '_';		/* replace leading dot with _ */						else							*cp++ = '.';						break;								case '/':	if (cp != buf)							*cp++ = ':';		/* replace / with : */						slashSeen = true;						break;			case ':':	*cp++ = '/';			/* replace : with / */						break;			default:	*cp++ = *tp;						break;		}	}	*cp = '\0';		if (slashSeen)		(void) strcat(macfilename, buf);	else		(void) strcpy(macfilename, buf);}void unshar(char *s){  char		buffer[BUFSIZ];  char		*cp;  FILE		*infp, *outfp;  char		unixfilename[256], mpwfilename[256];  char		*tp, *ts, delim;  char		terminator[30];  int		tlen;  int		line;  long		dirID, dID;  short		volRef;  FInfo		fileInfo;  OSErr		err;#ifndef	MPW  WDPBRec	wdpb;  char		lineNo[10];#endif#ifdef	MPW  if (strcmp(s, "-") == 0) {  	infp = stdin;	ErrMsg("\pProcessing std input:","");  }  else {	  infp = fopen(s, "r");	  CPSTR(s);	  if (!infp) {		ErrMsg("\pCould not open file", s);		exit(-1);	  }	  else ErrMsg("\pProcessing", s);  }#else	/* Think C */  infp = fopen(s, "r");  CPSTR(s);  if (!infp) {    ErrMsg((char*)"\pCould not open file", s);    return;  }#endif  /* skip over news header lines etc. */  for (line = 1; cp = fgets(buffer, sizeof(buffer), infp); line++)    if (buffer[0] == '#' || buffer[0] == ':') break;	  if (!cp) {    ErrMsg((char*)"\pCould not locate start of archive in file", s);    exit(-1);  }#ifndef	MPW  /* Think C version needs to ask user to locate target folder */  /*   * Open the target directory as a base to put everything.   */  if ((dirID = GetDir((char*)"\pin which to put extracted files")) == 0L)	  return;    dID = dirID;			/* dID may be changed if subfolders are created */  (void)HSetVol(NIL, dirVRefNum, dirID);    /* now we should be at the start of the shar archive itself */  infoDlgPtr = NIL;  infoDlgPtr = GetNewDialog (infoDlgRes, NIL, (WindowPtr)-1L);#endif  HGetVol((StringPtr)buffer, &volRef, &dID);		/* get current dir, vol */  while (cp = fgets(buffer, sizeof(buffer), infp)) {    line++;#ifdef	MPW	SpinCursor(-1);#endif	MPW    if (buffer[0] == '#' || buffer[0] == ':') continue;    /* comment line */    if (strncmp(buffer, "exit", 4) == 0) { break; }  /* exit */	/*	there are 2 types of shar files:		a) the ones that use "cat"		b) the ones that use "sed"	*/	if (strncmp(buffer, "sed", 3) == 0 ||		strncmp(buffer, "cat", 3) == 0) {		  sscanf(buffer, "%*[^>]>%s", unixfilename);		  /* make Mac relative pathname */		  ConvertFName(unixfilename, mpwfilename);		  /* work out the terminating string */		  sscanf(buffer, "%*[^<]<<%s", terminator);		  tp = terminator;		  tlen = strlen(terminator);		  		  while (*tp == ' ') tp++;		/* skip whitespace */		  switch (*tp) {			  case '\\':	ts = tp + 1;	/* start of term string */							delim = ' ';							break;			  case '"':		ts = tp + 1;							delim = *tp;							break;			  case '\'':	ts = tp + 1;							delim = *tp;							break;			  default:		ts = tp;							delim = ' ';							break;						  }		  do {			  tp++;		  } while (*tp != '\0' && *tp != delim);		  *tp = '\0';		  tlen = tp - ts;		  outfp = fopen(mpwfilename, "r");          CPSTR(mpwfilename);		  if (outfp && !force) {			fclose(outfp);#ifdef	MPW			ErrMsg("\pWill not overwrite existing file", mpwfilename);#else            ParamText((StringPtr)"\pWill not overwrite existing file",                	  (StringPtr)mpwfilename, (StringPtr)"\p", (StringPtr)"\p");            if (NoteAlert(dupFNAlertRes, NIL) == quitB) break;#endif			while (strncmp(buffer, ts, tlen) != 0) {  /* skip to terminating string */			  fgets(buffer, sizeof(buffer), infp);			  line++;			}		  } else {			if (outfp) fclose(outfp);            if (HCreate(volRef, dID, (StringPtr)mpwfilename, fdCreator, fdType) != noErr) {				ErrMsg((char*)"\pCouldn't create", mpwfilename);				exit(-1);			}#ifdef MPW			fprintf(stderr, "  Open \"%P\"\n", mpwfilename);#else            ParamText((StringPtr)mpwfilename,            		  (StringPtr)"\p", (StringPtr)"\p", (StringPtr)"\p");            SelectWindow(infoDlgPtr);            ShowWindow(infoDlgPtr);            DrawDialog(infoDlgPtr);         #endif            PCSTR((StringPtr)mpwfilename);			if ((outfp = fopen(mpwfilename, "w")) == NULL) {				CPSTR(mpwfilename);				ErrMsg((char*)"\pCouldn't open", mpwfilename);				exit(-1);			}						if (strncmp(buffer, "sed", 3) == 0) {				fgets(buffer, sizeof(buffer), infp);				do {				  fputs(buffer+1, outfp);				  fgets(buffer, sizeof(buffer), infp);				  line++;#ifdef	MPW				  SpinCursor(1);#endif	MPW				} while (strncmp(buffer, ts, tlen) != 0);			}			else {				/* copy everything up to terminating string to output file */				fgets(buffer, sizeof(buffer), infp);				while (strncmp(buffer, ts, tlen) != 0) {					fputs(buffer, outfp);					fgets(buffer, sizeof(buffer), infp);					line++;#ifdef	MPW					SpinCursor(1);#endif	MPW				}			}			fclose(outfp);#ifndef	MPW			HideWindow((WindowPtr)infoDlgPtr);#endif			/* set file type and creator */			CPSTR(mpwfilename);			GetFInfo((StringPtr)mpwfilename,0,&fileInfo);			fileInfo.fdType = fdType;			fileInfo.fdCreator = fdCreator;			SetFInfo((StringPtr)mpwfilename, 0, &fileInfo);			PCSTR((StringPtr)mpwfilename);		  }      } else if (strncmp(buffer, "if test ! -d", 12) == 0 ||      			 strncmp(buffer, "if `test ! -d", 13) == 0) {        /* testing to see if a directory is there */        if (sscanf(buffer, "if test ! -d '%s'", unixfilename) == 1 ||			sscanf(buffer, "if test ! -d %s", unixfilename) == 1 ||        	sscanf(buffer, "if `test ! -d %s`", unixfilename) == 1) {          /* make Mac relative pathname */		  ConvertFName(unixfilename, mpwfilename);					  /* I wish MPW C had mkdir(), but at least we don't have to do parameter blocks */		  CPSTR(unixfilename);		  CPSTR(mpwfilename);		  HGetVol((StringPtr)unixfilename, &volRef, &dirID);		  err = DirCreate(volRef, dirID, (StringPtr)mpwfilename, &dID);		}	} else if (strncmp(buffer, "cd ", 3) == 0) {		if (sscanf(buffer, "cd '%s'", unixfilename) == 1 ||			sscanf(buffer, "cd %s", unixfilename) == 1) 	{			/* change the default directory */			ConvertFName(unixfilename, mpwfilename);		 	CPSTR(mpwfilename);#ifdef MPW			fprintf(stderr, "###  Change directory to \"%P\"\n", mpwfilename);#endif			HSetVol((StringPtr)mpwfilename, 0, 0L);		    HGetVol((StringPtr)unixfilename, &volRef, &dID);		}	}  }  fclose(infp);}