/*
CopyPath.m -- Services program for Workspace to make a cd command
	using the current path, and copy that onto pasteboard in
	plain ASCII.
	This is for pasting in a cd command into a shell.
	Copy in Workspace doesn't work for this purpose, because
	the copied pathname is NXFilenamePboardType, rather than
	NXAsciiPboardType required by Terminal and Stuart.

	This app has no GUI.

	No Copyright is claimed.
	This program is hereby released into the public domain.

10-30-91, Version 1.0  Izumi Ohzawa.
11-07-91, Condensed and modified by Daniel Faken - Main differences:
	 1) Optimised. (You ask Why? Why not.)
	 3) No Workspace-owned icon-tile pops up, due to a .daemon extension.
	 4) Doesn't exit after pasting. (Can't see it anyhow, so why exit?)
	 5) I ask that you keep at least brief mention of my name on this.
11-12-91, Version 1.1 Izumi Ohzawa.
	Grafted back separate cd command making code, etc.
11-20-91, Version 1.2 Eric P. Scott.
	Removed unused .h file.
	Removed all references to Application Class.
	Removed Window posing nonsense.
	Converted internal method to C procedure.
	Added more intelligent shell quoting.
	Added pushd option.
1-08-92, Version 1.3 Michael Cohen (mcohen@acoustic-srx.ntt.jp)
	Fixed for multiple browser selection.
*/

static char sccsid[]="@(#)CopyPath.m	1.3 (NTT, Tokyo) 1/8/92";

#import <stdlib.h>
#import <appkit/Listener.h>
#import <appkit/Pasteboard.h>
#import <appkit/Speaker.h>
#import <strings.h>
#import <sys/stat.h>
#import <sys/param.h>

char PathBuf[2*MAXPATHLEN+8];

@interface CopyPath:Object
- copyAsciiPath:(id)pbid userData:(const char *)udata error:(char **)errmsg;
- makeCdCommand:(id)pbid userData:(const char *)udata error:(char **)errmsg;
- makePushdCommand:(id)pbid userData:(const char *)udata error:(char **)errmsg;
@end

@implementation CopyPath

// mutant strcat that quotes shell metacharacters
static char *shstrcat(register char *dst, register char *src)
{
    register char *p;
    
    while (*dst) dst++;
    p=src;
    if (*p=='~') *dst++='\\';	// csh "feature"
    while (*p) {
	if (*p<=' '||*p>'~'||index(";&()|<>\\'\"$*?[]^!", *p)) *dst++='\\';
	*dst++=*p++;
    }
    *dst='\0';
    return(src);
}

// Real procedure to do work
static void addPath(id pbid, const char *udata, char **errmsg, int flag)
{
char *path, *pathptr, *tabLoc; 
int   length, i;
char **types;
struct stat filestat;
static id spkr;

	PathBuf[0]='\0';
	if(flag) (void)strcpy(PathBuf, flag==2 ? "pushd " : "cd ");

	(const char *const *)types = [pbid types];
	for(i = 0; strcmp(types[i], NXFilenamePboardType) && types[i]; i++) ;
	if(types[i])
	{
	    [pbid readType:NXFilenamePboardType data:&path length:&length];
	    
	    // This isolates the first of possibly multiple paths
	    if (tabLoc = index(path, '\t'))
		*tabLoc = '\0';

	    if(path && length && !stat(path, &filestat))
	    {
		// If not a directory, make it into a directory when making cd
		// command.  Note that stat() follows symlinks.
		if(flag) {
		    if((filestat.st_mode & S_IFMT) != S_IFDIR)
		    {
			pathptr = rindex(path, '/');
			if(pathptr && path != pathptr) *pathptr = '\0';	
		    }
		    
		    (void)shstrcat(PathBuf, path);
		}
		else
		    (void)strcat(PathBuf, path);
		[[[Pasteboard new]
		   declareTypes:&NXAsciiPboardType num:1 owner:NULL]
		   writeType:NXAsciiPboardType data:PathBuf
		   length:strlen(PathBuf)];
	    } /* end if(path && length && ... ) */
	    else
		*errmsg = "Nothing found on pasteboard.";
	    vm_deallocate(task_self(), path, length);
	}  /* end if(types[i]) */
	else
	    *errmsg = "No filename/pathname found on pasteboard.";
}

// Just puts path to ascii paste board
- copyAsciiPath:(id)pbid userData:(const char *)udata error:(char **)errmsg
{
    addPath(pbid, udata, errmsg, 0);
    return self;
}

// Prepends "cd " to path and quotes as needed
- makeCdCommand:(id)pbid userData:(const char *)udata error:(char **)errmsg
{
    addPath(pbid, udata, errmsg, 1);
    return self;
}

// Prepends "pushd " to path and quotes as needed
- makePushdCommand:(id)pbid userData:(const char *)udata error:(char **)errmsg
{
    addPath(pbid, udata, errmsg, 2);
    return self;
}

@end


int main(int argc, char *argv[])
{
    id lid;

    if (argc>1) _exit(0);		// daemons aren't launched with args!

    lid = [[Listener alloc] init];
    [lid setServicesDelegate:[[CopyPath alloc] init]];
    if ([lid checkInAs:"CopyPath"]) _exit(1);	// uh-oh!
    [lid addPort];
    [Listener run];	// never returns
    return 0;
}
