sl@0: /* 
sl@0:  * tclMacUnix.c --
sl@0:  *
sl@0:  *	This file contains routines to implement several features
sl@0:  *	available to the Unix implementation, but that require
sl@0:  *      extra work to do on a Macintosh.  These include routines
sl@0:  *      Unix Tcl normally hands off to the Unix OS.
sl@0:  *
sl@0:  * Copyright (c) 1993-1994 Lockheed Missle & Space Company, AI Center
sl@0:  * Copyright (c) 1994-1997 Sun Microsystems, Inc.
sl@0:  *
sl@0:  * See the file "license.terms" for information on usage and redistribution
sl@0:  * of this file, and for a DISCLAIMER OF ALL WARRANTIES.
sl@0:  *
sl@0:  * RCS: @(#) $Id: tclMacUnix.c,v 1.5 2002/10/09 11:54:45 das Exp $
sl@0:  */
sl@0: 
sl@0: #include <Files.h>
sl@0: #include <Strings.h>
sl@0: #include <TextUtils.h>
sl@0: #include <Finder.h>
sl@0: #include <FSpCompat.h>
sl@0: #include <Aliases.h>
sl@0: #include <Errors.h>
sl@0: 
sl@0: #include "tclInt.h"
sl@0: #include "tclMacInt.h"
sl@0: 
sl@0: /*
sl@0:  * The following two Includes are from the More Files package
sl@0:  */
sl@0: #include "FileCopy.h"
sl@0: #include "MoreFiles.h"
sl@0: #include "MoreFilesExtras.h"
sl@0: 
sl@0: /*
sl@0:  * The following may not be defined in some versions of
sl@0:  * MPW header files.
sl@0:  */
sl@0: #ifndef kIsInvisible
sl@0: #define kIsInvisible 0x4000
sl@0: #endif
sl@0: #ifndef kIsAlias
sl@0: #define kIsAlias 0x8000
sl@0: #endif
sl@0: 
sl@0: /*
sl@0:  * Missing error codes
sl@0:  */
sl@0: #define usageErr		500
sl@0: #define noSourceErr		501
sl@0: #define isDirErr		502
sl@0: 
sl@0: 
sl@0: /*
sl@0:  *----------------------------------------------------------------------
sl@0:  *
sl@0:  * Tcl_EchoCmd --
sl@0:  *
sl@0:  *    Implements the TCL echo command:
sl@0:  *        echo ?str ...?
sl@0:  *
sl@0:  * Results:
sl@0:  *      Always returns TCL_OK.
sl@0:  *
sl@0:  * Side effects:
sl@0:  *	None.
sl@0:  *
sl@0:  *----------------------------------------------------------------------
sl@0:  */
sl@0: 
sl@0: int
sl@0: Tcl_EchoCmd(
sl@0:     ClientData dummy,			/* Not used. */
sl@0:     Tcl_Interp *interp,			/* Current interpreter. */
sl@0:     int argc,				/* Number of arguments. */
sl@0:     CONST char **argv)			/* Argument strings. */
sl@0: {
sl@0:     Tcl_Channel chan;
sl@0:     int mode, result, i;
sl@0: 
sl@0:     chan = Tcl_GetChannel(interp, "stdout", &mode);
sl@0:     if (chan == (Tcl_Channel) NULL) {
sl@0:         return TCL_ERROR;
sl@0:     }
sl@0:     for (i = 1; i < argc; i++) {
sl@0: 	result = Tcl_WriteChars(chan, argv[i], -1);
sl@0: 	if (result < 0) {
sl@0: 	    Tcl_AppendResult(interp, "echo: ", Tcl_GetChannelName(chan),
sl@0: 		    ": ", Tcl_PosixError(interp), (char *) NULL);
sl@0: 	    return TCL_ERROR;
sl@0: 	}
sl@0:         if (i < (argc - 1)) {
sl@0: 	    Tcl_WriteChars(chan, " ", -1);
sl@0: 	}
sl@0:     }
sl@0:     Tcl_WriteChars(chan, "\n", -1);
sl@0:     return TCL_OK;
sl@0: }
sl@0: 
sl@0: /*
sl@0:  *----------------------------------------------------------------------
sl@0:  *
sl@0:  * Tcl_LsObjCmd --
sl@0:  *
sl@0:  *	This procedure is invoked to process the "ls" Tcl command.
sl@0:  *	See the user documentation for details on what it does.
sl@0:  *
sl@0:  * Results:
sl@0:  *	A standard Tcl result.
sl@0:  *
sl@0:  * Side effects:
sl@0:  *	See the user documentation.
sl@0:  *
sl@0:  *----------------------------------------------------------------------
sl@0:  */
sl@0: int
sl@0: Tcl_LsObjCmd(
sl@0:     ClientData dummy,			/* Not used. */
sl@0:     Tcl_Interp *interp,			/* Current interpreter. */
sl@0:     int objc,				/* Number of arguments. */
sl@0:     Tcl_Obj *CONST objv[])		/* Argument strings. */
sl@0: {
sl@0: #define STRING_LENGTH 80
sl@0: #define CR '\n'
sl@0:     int i, j;
sl@0:     int fieldLength, len = 0, maxLen = 0, perLine;
sl@0:     OSErr err;
sl@0:     CInfoPBRec paramBlock;
sl@0:     HFileInfo *hpb = (HFileInfo *)&paramBlock;
sl@0:     DirInfo *dpb = (DirInfo *)&paramBlock;
sl@0:     char theFile[256];
sl@0:     char theLine[STRING_LENGTH + 2];
sl@0:     int fFlag = false, pFlag = false, aFlag = false, lFlag = false,
sl@0: 	cFlag = false, hFlag = false;
sl@0:     char *argv;
sl@0:     Tcl_Obj *newObjv[2], *resultObjPtr;
sl@0: 
sl@0:     /*
sl@0:      * Process command flags.  End if argument doesn't start
sl@0:      * with a dash or is a dash by itself.  The remaining arguments
sl@0:      * should be files.
sl@0:      */
sl@0:     for (i = 1; i < objc; i++) {
sl@0:     	argv = Tcl_GetString(objv[i]);
sl@0: 	if (argv[0] != '-') {
sl@0: 	    break;
sl@0: 	}
sl@0: 		
sl@0: 	if (!strcmp(argv, "-")) {
sl@0: 	    i++;
sl@0: 	    break;
sl@0: 	}
sl@0: 		
sl@0: 	for (j = 1 ; argv[j] ; ++j) {
sl@0: 	    switch(argv[j]) {
sl@0: 	    case 'a':
sl@0: 	    case 'A':
sl@0: 		aFlag = true;
sl@0: 		break;
sl@0: 	    case '1':
sl@0: 		cFlag = false;
sl@0: 		break;
sl@0: 	    case 'C':
sl@0: 		cFlag = true;
sl@0: 		break;
sl@0: 	    case 'F':
sl@0: 		fFlag = true;
sl@0: 		break;
sl@0: 	    case 'H':
sl@0: 		hFlag = true;
sl@0: 		break;
sl@0: 	    case 'p':
sl@0: 		pFlag = true;
sl@0: 		break;
sl@0: 	    case 'l':
sl@0: 		pFlag = false;
sl@0: 		lFlag = true;
sl@0: 		break;
sl@0: 	    default:
sl@0: 		Tcl_AppendResult(interp, "error - unknown flag ",
sl@0: 			"usage: ls -apCFHl1 ?files? ", NULL);
sl@0: 		return TCL_ERROR;
sl@0: 	    }
sl@0: 	}
sl@0:     }
sl@0: 
sl@0:     objv += i;
sl@0:     objc -= i;
sl@0: 
sl@0:     /*
sl@0:      * No file specifications means we search for all files.
sl@0:      * Glob will be doing most of the work.
sl@0:      */
sl@0:      if (!objc) {
sl@0: 	objc = 1;
sl@0: 	newObjv[0] = Tcl_NewStringObj("*", -1);
sl@0: 	newObjv[1] = NULL;
sl@0: 	objv = newObjv;
sl@0:     }
sl@0: 
sl@0:     if (Tcl_GlobObjCmd(NULL, interp, objc + 1, objv - 1) != TCL_OK) {
sl@0:     	Tcl_ResetResult(interp);
sl@0:     	return TCL_ERROR;
sl@0:     }
sl@0: 
sl@0:     resultObjPtr = Tcl_GetObjResult(interp);
sl@0:     Tcl_IncrRefCount(resultObjPtr);
sl@0:     if (Tcl_ListObjGetElements(interp, resultObjPtr, &objc, (Tcl_Obj ***)&objv) != TCL_OK) {
sl@0:     	Tcl_DecrRefCount(resultObjPtr);
sl@0:     	return TCL_ERROR;
sl@0:     }
sl@0: 
sl@0:     Tcl_ResetResult(interp);
sl@0: 
sl@0:     /*
sl@0:      * There are two major methods for listing files: the long
sl@0:      * method and the normal method.
sl@0:      */
sl@0:     if (lFlag) {
sl@0: 	char	creator[5], type[5], time[16], date[16];
sl@0: 	char	lineTag;
sl@0: 	long	size;
sl@0: 	unsigned short flags;
sl@0: 	Tcl_Obj *objPtr;
sl@0: 	char *string;
sl@0: 	int length;
sl@0: 
sl@0: 	/*
sl@0: 	 * Print the header for long listing.
sl@0: 	 */
sl@0: 	if (hFlag) {
sl@0: 	    sprintf(theLine, "T %7s %8s %8s %4s %4s %6s %s",
sl@0: 		    "Size", "ModTime", "ModDate",
sl@0: 		    "CRTR", "TYPE", "Flags", "Name");
sl@0: 	    Tcl_AppendResult(interp, theLine, "\n", NULL);
sl@0: 	    Tcl_AppendResult(interp,
sl@0: 		    "-------------------------------------------------------------\n",
sl@0: 		    NULL);
sl@0: 	}
sl@0: 		
sl@0: 	for (i = 0; i < objc; i++) {
sl@0: 	    strcpy(theFile, Tcl_GetString(objv[i]));
sl@0: 			
sl@0: 	    c2pstr(theFile);
sl@0: 	    hpb->ioCompletion = NULL;
sl@0: 	    hpb->ioVRefNum = 0;
sl@0: 	    hpb->ioFDirIndex = 0;
sl@0: 	    hpb->ioNamePtr = (StringPtr) theFile;
sl@0: 	    hpb->ioDirID = 0L;
sl@0: 	    err = PBGetCatInfoSync(&paramBlock);
sl@0: 	    p2cstr((StringPtr) theFile);
sl@0: 
sl@0: 	    if (hpb->ioFlAttrib & 16) {
sl@0: 		/*
sl@0: 		 * For directories use zero as the size, use no Creator
sl@0: 		 * type, and use 'DIR ' as the file type.
sl@0: 		 */
sl@0: 		if ((aFlag == false) && (dpb->ioDrUsrWds.frFlags & 0x1000)) {
sl@0: 		    continue;
sl@0: 		}
sl@0: 		lineTag = 'D';
sl@0: 		size = 0;
sl@0: 		IUTimeString(dpb->ioDrMdDat, false, (unsigned char *)time);
sl@0: 		p2cstr((StringPtr)time);
sl@0: 		IUDateString(dpb->ioDrMdDat, shortDate, (unsigned char *)date);
sl@0: 		p2cstr((StringPtr)date);
sl@0: 		strcpy(creator, "    ");
sl@0: 		strcpy(type, "DIR ");
sl@0: 		flags = dpb->ioDrUsrWds.frFlags;
sl@0: 		if (fFlag || pFlag) {
sl@0: 		    strcat(theFile, ":");
sl@0: 		}
sl@0: 	    } else {
sl@0: 		/*
sl@0: 		 * All information for files should be printed.  This
sl@0: 		 * includes size, modtime, moddate, creator type, file
sl@0: 		 * type, flags, anf file name.
sl@0: 		 */
sl@0: 		if ((aFlag == false) &&
sl@0: 			(hpb->ioFlFndrInfo.fdFlags & kIsInvisible)) {
sl@0: 		    continue;
sl@0: 		}
sl@0: 		lineTag = 'F';
sl@0: 		size = hpb->ioFlLgLen + hpb->ioFlRLgLen;
sl@0: 		IUTimeString(hpb->ioFlMdDat, false, (unsigned char *)time);
sl@0: 		p2cstr((StringPtr)time);
sl@0: 		IUDateString(hpb->ioFlMdDat, shortDate, (unsigned char *)date);
sl@0: 		p2cstr((StringPtr)date);
sl@0: 		strncpy(creator, (char *) &hpb->ioFlFndrInfo.fdCreator, 4);
sl@0: 		creator[4] = 0;
sl@0: 		strncpy(type, (char *) &hpb->ioFlFndrInfo.fdType, 4);
sl@0: 		type[4] = 0;
sl@0: 		flags = hpb->ioFlFndrInfo.fdFlags;
sl@0: 		if (fFlag) {
sl@0: 		    if (hpb->ioFlFndrInfo.fdFlags & kIsAlias) {
sl@0: 			strcat(theFile, "@");
sl@0: 		    } else if (hpb->ioFlFndrInfo.fdType == 'APPL') {
sl@0: 			strcat(theFile, "*");
sl@0: 		    }
sl@0: 		}
sl@0: 	    }
sl@0: 			
sl@0: 	    sprintf(theLine, "%c %7ld %8s %8s %-4.4s %-4.4s 0x%4.4X %s",
sl@0: 		    lineTag, size, time, date, creator, type, flags, theFile);
sl@0: 						 
sl@0: 	    Tcl_AppendResult(interp, theLine, "\n", NULL);
sl@0: 	    
sl@0: 	}
sl@0: 		
sl@0: 	objPtr = Tcl_GetObjResult(interp);
sl@0: 	string = Tcl_GetStringFromObj(objPtr, &length);
sl@0: 	if ((length > 0) && (string[length - 1] == '\n')) {
sl@0: 	    Tcl_SetObjLength(objPtr, length - 1);
sl@0: 	}
sl@0:     } else {
sl@0: 	/*
sl@0: 	 * Not in long format. We only print files names.  If the
sl@0: 	 * -C flag is set we need to print in multiple coloumns.
sl@0: 	 */
sl@0: 	int argCount, linePos;
sl@0: 	Boolean needNewLine = false;
sl@0: 
sl@0: 	/*
sl@0: 	 * Fiend the field length: the length each string printed
sl@0: 	 * to the terminal will be.
sl@0: 	 */
sl@0: 	if (!cFlag) {
sl@0: 	    perLine = 1;
sl@0: 	    fieldLength = STRING_LENGTH;
sl@0: 	} else {
sl@0: 	    for (i = 0; i < objc; i++) {
sl@0: 	    	argv = Tcl_GetString(objv[i]);
sl@0: 		len = strlen(argv);
sl@0: 		if (len > maxLen) {
sl@0: 		    maxLen = len;
sl@0: 		}
sl@0: 	    }
sl@0: 	    fieldLength = maxLen + 3;
sl@0: 	    perLine = STRING_LENGTH / fieldLength;
sl@0: 	}
sl@0: 
sl@0: 	argCount = 0;
sl@0: 	linePos = 0;
sl@0: 	memset(theLine, ' ', STRING_LENGTH);
sl@0: 	while (argCount < objc) {
sl@0: 	    strcpy(theFile, Tcl_GetString(objv[argCount]));
sl@0: 			
sl@0: 	    c2pstr(theFile);
sl@0: 	    hpb->ioCompletion = NULL;
sl@0: 	    hpb->ioVRefNum = 0;
sl@0: 	    hpb->ioFDirIndex = 0;
sl@0: 	    hpb->ioNamePtr = (StringPtr) theFile;
sl@0: 	    hpb->ioDirID = 0L;
sl@0: 	    err = PBGetCatInfoSync(&paramBlock);
sl@0: 	    p2cstr((StringPtr) theFile);
sl@0: 
sl@0: 	    if (hpb->ioFlAttrib & 16) {
sl@0: 		/*
sl@0: 		 * Directory. If -a show hidden files.  If -f or -p
sl@0: 		 * denote that this is a directory.
sl@0: 		 */
sl@0: 		if ((aFlag == false) && (dpb->ioDrUsrWds.frFlags & 0x1000)) {
sl@0: 		    argCount++;
sl@0: 		    continue;
sl@0: 		}
sl@0: 		if (fFlag || pFlag) {
sl@0: 		    strcat(theFile, ":");
sl@0: 		}
sl@0: 	    } else {
sl@0: 		/*
sl@0: 		 * File: If -a show hidden files, if -f show links
sl@0: 		 * (aliases) and executables (APPLs).
sl@0: 		 */
sl@0: 		if ((aFlag == false) &&
sl@0: 			(hpb->ioFlFndrInfo.fdFlags & kIsInvisible)) {
sl@0: 		    argCount++;
sl@0: 		    continue;
sl@0: 		}
sl@0: 		if (fFlag) {
sl@0: 		    if (hpb->ioFlFndrInfo.fdFlags & kIsAlias) {
sl@0: 			strcat(theFile, "@");
sl@0: 		    } else if (hpb->ioFlFndrInfo.fdType == 'APPL') {
sl@0: 			strcat(theFile, "*");
sl@0: 		    }
sl@0: 		}
sl@0: 	    }
sl@0: 
sl@0: 	    /*
sl@0: 	     * Print the item, taking into account multi-
sl@0: 	     * coloum output.
sl@0: 	     */
sl@0: 	    strncpy(theLine + (linePos * fieldLength), theFile,
sl@0: 		    strlen(theFile));
sl@0: 	    linePos++;
sl@0: 			
sl@0: 	    if (linePos == perLine) {
sl@0: 		theLine[STRING_LENGTH] = '\0';
sl@0: 		if (needNewLine) {
sl@0: 		    Tcl_AppendResult(interp, "\n", theLine, NULL);
sl@0: 		} else {
sl@0: 		    Tcl_AppendResult(interp, theLine, NULL);
sl@0: 		    needNewLine = true;
sl@0: 		}
sl@0: 		linePos = 0;
sl@0: 		memset(theLine, ' ', STRING_LENGTH);
sl@0: 	    }
sl@0: 			
sl@0: 	    argCount++;
sl@0: 	}
sl@0: 		
sl@0: 	if (linePos != 0) {
sl@0: 	    theLine[STRING_LENGTH] = '\0';
sl@0: 	    if (needNewLine) {
sl@0: 		Tcl_AppendResult(interp, "\n", theLine, NULL);
sl@0: 	    } else {
sl@0: 		Tcl_AppendResult(interp, theLine, NULL);
sl@0: 	    }
sl@0: 	}
sl@0:     }
sl@0: 
sl@0:     Tcl_DecrRefCount(resultObjPtr);
sl@0:     	
sl@0:     return TCL_OK;
sl@0: }