/*
 * cc -- unix-like compiler driver for amiga lattice c
 *
 * adapted for lattice version 5.0 and extensively modified
 *   by Miles Bader, from the original by Fred Fish (copyright
 *   follows).
 */

/************************************************************************
 *									*
 *			Copyright (c) 1985, Fred Fish			*
 *			    All Rights Reserved				*
 *									*
 *	This software and/or documentation is released into the		*
 *	public domain for personal, non-commercial use only.		*
 *	Limited rights to use, modify, and redistribute are hereby	*
 *	granted for non-commercial purposes, provided that all		*
 *	copyright notices remain intact and all changes are clearly	*
 *	documented.  The author makes no warranty of any kind with	*
 *	respect to this product and explicitly disclaims any implied	*
 *	warranties of merchantability or fitness for any particular	*
 *	purpose.							*
 *									*
 ************************************************************************
 */

#include <stdio.h>
#include <stdlib.h>
#include <string.h>

#include <dos.h>
#include <proto/dos.h>
#include <ios1.h> /* magic */

#include "common.h"
#include "list.h"
#include "dstr.h"
#include "op.h"
#include "options.h"

#include "config.h"

static char *locate(char *file,struct list *list);	/* find a file */
static void cleanObjects(int mustExist);	/* Remove .o for link and go mode */
static int makeObjs();			/* Reduce to list of object files */
static int parseCommandLine();		/* Deal with command line */
static int compile(struct op *op);	/* Translate from .c to .o */
static int assemble(struct op *op);	/* Translate from .s to .o */
static int link();			/* Gather .o's into executable */
static int runCmd(struct dstr *cmd);

#ifdef amiga
#define CHECK_ABORT chkabort()
#else
#define CHECK_ABORT				/* NULL expansion */
#endif	/* amiga */
 
DBUG_GLOBALS

/*
 *	flags set by command line arguments
 */
static int debugLevel=DEFAULT_DEBUGLEVEL;	/* -g (debugging level) */

struct list *libDirs=NULL;
struct list *incDirs=NULL;
struct list *binDirs=NULL;
struct list *defines=NULL;
struct list *undefines=NULL;
struct list *libs=NULL;

struct list *pass1PassThrus=NULL;
struct list *pass2PassThrus=NULL;
struct list *optPassThrus=NULL;
struct list *linkPassThrus=NULL;

struct list *ops=NULL;

static char *outfile=DEFAULT_EXEC;	/* output file name from linker */
static char *tempDir=TEMPDIR;		/* where to keep quad files */
static char *sinkFile=SINKFILE;		/* where to put output from passes */

struct dstr *cmdLine=NULL;
 
static int abortmsg()
{
    if(options_IsSet(OPT_ECHO))
	fprintf(stderr,"cc: aborting\n");
    return 1; /* abort */
}

static int cleanup(status)
int status;
{
    if(cmdLine!=NULL)	dstr_Free(cmdLine);

    if(libDirs!=NULL)	list_Free(libDirs);
    if(incDirs!=NULL)	list_Free(incDirs);
    if(binDirs!=NULL)	list_Free(binDirs);
    if(defines!=NULL)	list_Free(defines);
    if(undefines!=NULL)	list_Free(undefines);
    if(libs!=NULL)	list_Free(libs);

    if(pass1PassThrus!=NULL)	list_Free(pass1PassThrus);
    if(pass2PassThrus!=NULL)	list_Free(pass2PassThrus);
    if(optPassThrus!=NULL)	list_Free(optPassThrus);
    if(linkPassThrus!=NULL)	list_Free(linkPassThrus);

    if(ops!=NULL)	list_Free(ops);

    return status;
}

static void init()
{
    int *opt;

    options_Init();
    for(opt=defaultOptions; *opt!=0; opt++)
	options_Set(*opt);

    cmdLine=dstr_Create(30);		/* global command buffer */

    libDirs=list_Create((void **)libDirsInit);
    incDirs=list_Create((void **)incDirsInit);
    binDirs=list_Create((void **)binDirsInit);
    defines=list_Create((void **)definesInit);
    undefines=list_Create((void **)undefinesInit);
    libs=list_Create((void **)libsInit);

    pass1PassThrus=list_Create(NULL);
    pass2PassThrus=list_Create(NULL);
    optPassThrus=list_Create(NULL);
    linkPassThrus=list_Create(NULL);

    ops=list_Create(NULL);
    list_SetFree(ops,op_Free);
}

void main(argc, argv)
int argc;
char *argv[];
{
    int status;

    DBUG_ENTER("main");

    onexit(cleanup);
    init();

    onbreak(abortmsg);

    if(parseCommandLine(argc,argv)==0)
	fatal("no files specified");

    cleanObjects(FALSE);	/* get rid of existing object files if any */

    status=makeObjs();
    if(status==0 && options_IsSet(OPT_LINK)){
	status=link();
	if(list_GetLen(ops)==1)
	    cleanObjects(TRUE);
    }

    exit(status);
}

/*
 *	The following macro is used to allow optional whitespace between
 *	an option and it's argument.  Argp is left pointing at the option
 *	and argv and argc are adjusted accordingly if necessary.
 *
 *	Note that there is no check for missing option arguments.  In
 *	particular, -o -V will blindly take -V as the output file name.
 *
 */

#define XARG(argc,argv,argp) {if(*++argp=='\0'){argp=(*argv++);argc--;}}

static int parseCommandLine(argc,argv)
int argc;
char **argv;
{
    register char *argp;    

    DBUG_ENTER("parseCommandLine");

    argc--;
    argv++;
    while(argc-- > 0){
	CHECK_ABORT;

	argp=*argv++;

	if(*argp=='+')
	    options_SetByName(argp+1);
	else if(*argp!='-')    
	    list_Add(ops,(void *)op_Create(argp));
	else
	    switch(*++argp){
		case 'c':
		    options_Clear(OPT_LINK);	/* don't link object modules */
		    break;
		case 'p':			/* pass thru args to a pass */
		    switch(*++argp){
			case '1':		/* pass 1 */
			    XARG(argc,argv,argp);			
			    list_Add(pass1PassThrus,argp);
			    break;
			case '2':		/* pass 2 */
			    XARG(argc,argv,argp);			
			    list_Add(pass2PassThrus,argp);
			    break;
			case 'o':		/* optimizer */
			    XARG(argc,argv,argp);			
			    list_Add(optPassThrus,argp);
			    break;
			case 'l':		/* linking */
			    XARG(argc,argv,argp);			
			    list_Add(linkPassThrus,argp);
			    break;
			default:
			    fatal("unknown pass '%c'; must be one of [12ol]",argp[-1]);
		    }
		    break;
		case 'D':
		    XARG(argc,argv,argp);
		    list_Add(defines,(void *)argp);
		    break;
		case 'E':
		    warning("-E unimplemented, converted to -P instead");
		    /* fall through */
		case 'P':
		    options_Clear(OPT_COMPILE);
		    options_Clear(OPT_ASSEMBLE);
		    options_Clear(OPT_LINK);
		    break;
		case 'g':
		    if(*++argp!='\0') /* optional debuggin level */
			debugLevel=atoi(argp);
		    options_Set(OPT_DEBUG);
		    break;
		case 'I':
		    XARG(argc,argv,argp);
		    list_Add(incDirs,(void *)argp);
		    break;
		case 'l':
		    XARG(argc,argv,argp);
		    list_Add(libs,(void *)argp);
		    break;
		case 'L':
		    XARG(argc,argv,argp);
		    list_Add(libDirs,(void *)argp);
		    break;
		case 'B':
		    XARG(argc,argv,argp);
		    list_Add(binDirs,(void *)argp);
		    break;
		case 'O':
		    options_Set(OPT_OPTIMIZE);
		    break;
		case 'o':
		    XARG(argc,argv,argp);
		    outfile=argp;
		    break;
		case 't':		/* warning, non-standard */
		    XARG(argc,argv,argp);
		    tempDir=argp;
		    break;
		case 'S':
		    warning("-S option not yet implemented, ignored");
		    options_Clear(OPT_ASSEMBLE);
		    break;
		case 'U':
		    XARG(argc,argv,argp);
		    list_Add(undefines,(void *)argp);
		    break;
		default:
		    fatal("unknown option '%c'",*argp);
		    break;
	    }
    }

    DBUG_RETURN(int,list_GetLen(ops));
}

/*
 *	For each operand, do compilation or assembly as necessary, to
 *	reduce to an object file in the current directory.
 */
static int makeObjs()
{
    int n;
    void **els;
    int numOps=list_GetLen(ops);
    int status=0;

    DBUG_ENTER("makeObjs");

    for(n=numOps,els=list_GetEls(ops); n>0; n--,els++){
	struct op *op=(struct op *)*els;

	CHECK_ABORT;

	if(numOps>1 && (op_IsC(op) || op_IsS(op)))
	    printf("%s.%s:\n",op->rootname,op->suffix);

	if(op_IsC(op))
	    status=compile(op);
	else if(op_IsS(op))
	    status=assemble(op);
    }

    DBUG_RETURN(int,status);
}

/* handy little routine */
void addfList(ds,fmt,list)
struct dstr *ds;
char *fmt;
struct list *list;
{
    int n;
    void **els;

    for(n=list_GetLen(list),els=list_GetEls(list); n>0; n--,els++)
	dstr_Addf(ds,fmt,(char *)*els);
}

/*
 *	Note that commands to cc of the form "-l<name>" get interpreted
 *	to mean use a library called "name.lib" from the library
 *	directory.
 *      -lm is specially interpreted to be the appropriate math library.
 */
static int link()
{
    int n;
    void **els;

    DBUG_ENTER("link");

    dstr_Clear(cmdLine);

    dstr_Addf(cmdLine,"%s ",locate("blink",binDirs));

    if(options_IsSet(OPT_DETACH))
	dstr_Append(cmdLine,locate("cback.o",libDirs));
    else if(options_IsSet(OPT_RESIDENT)){
	if(options_IsSet(OPT_CATCH))
	    dstr_Append(cmdLine,locate("catchres.o",libDirs));
	else
	    dstr_Append(cmdLine,locate("cres.o",libDirs));
    }else if(options_IsSet(OPT_CATCH))
	dstr_Append(cmdLine,locate("catch.o",libDirs));
    else
	dstr_Append(cmdLine,locate("c.o",libDirs));

    for(n=list_GetLen(ops),els=list_GetEls(ops); n>0; n--,els++){
	struct op *op=(struct op *)*els;
	char *name=(op_IsO(op) ? op->rootname : op->basename);

	dstr_Addf(cmdLine,"+%s.o",name);
    }

    if(options_IsSet(OPT_TINYMAIN))
	dstr_Addf(cmdLine," define __main=__tinymain ");

    dstr_Addf(cmdLine," library ");
    for(n=list_GetLen(libs),els=list_GetEls(libs); n>0; n--,els++){
	char buffer[50];
	char *lib=(char *)*els;

	if(strcmp(lib,"m")==0){
	    /* map -lm to appropiate library */

	    strcpy(buffer,"lib:lcm");

	    if(options_IsSet(OPT_FFP))
	        strcat(buffer,"ffp");
	    else if(options_IsSet(OPT_IEEE))
	 	strcat(buffer,"ieee");
	    else if(options_IsSet(OPT_881))
		strcat(buffer,"881");

	    /*
	     * there are only r and s libraries for lcm.lib in the lattice
	     * distribution; if there is a way to get the lcmieee, lcmffp,
	     * & lcm881 libraries to work with reg-args & short-ints, I
	     * couldn't find it.
	     */
	    if(options_IsSet(OPT_SHORTINTS))
	        strcat(buffer,"s");
	    if(options_IsSet(OPT_REGARGS))
	        strcat(buffer,"r");

	    strcat(buffer,".lib");
	}else if(strcmp(lib,"lc")==0){
	    /* do the standard c library */

	    strcpy(buffer,"lib:lc");

	    if(options_IsSet(OPT_SHORTINTS))
	        strcat(buffer,"s");
	    if(options_IsSet(OPT_REGARGS))
	    	strcat(buffer,"r");
	    if(options_IsSet(OPT_ABSDATA))
	        strcat(buffer,"nb");

	    strcat(buffer,".lib");

	    if(options_IsSet(OPT_REGARGS)){
	        /*
	         * Now, do it AGAIN, but without registers, to catch any missing refs.
	         * I don't think this is exactly the right thing to do, but there seem
	         * to be internal functions missing from the register-fied libraries
	         */

	        strcat(buffer,"+lib:lc");

	        if(options_IsSet(OPT_SHORTINTS))
	            strcat(buffer,"s");
	        if(options_IsSet(OPT_ABSDATA))
	            strcat(buffer,"nb");

	        strcat(buffer,".lib");
	    }
	}else{
	    sprintf(buffer,"%s.lib",lib);
	    strcpy(buffer,locate(buffer,libDirs));
	}	    

        if(n==1)
	    dstr_Append(cmdLine,buffer);
	else
	    dstr_Addf(cmdLine,"%s+",buffer);
    }

    if(outfile==NULL)
	/* there better be at least one source file! */
	outfile=((struct op *)list_GetEls(ops))->basename;

    dstr_Addf(cmdLine," to %s map nil:",outfile);

    addfList(cmdLine," %s",linkPassThrus);

    DBUG_RETURN(int,runCmd(cmdLine));
}

/*
 *	compile one operand from a C source program to an object module.
 */
static int compile(op)
struct op *op;
{
    int status=0;

    DBUG_ENTER("compile");

    if(options_IsSet(OPT_ASSEMBLE)){
	status=pass1(op);
	if(status==0 && options_IsSet(OPT_COMPILE)){
	    CHECK_ABORT;
	    if(options_IsSet(OPT_OPTIMIZE))
		status=optimize(op);
	    if(status==0)
	        status=pass2(op);
	}
    }

    DBUG_RETURN(int,status);
}

/*
 *	Note that because of brain-damage in the fact that -p to lc1 removes
 *	all predefined defs, we must add them so replacing -c with -P in the
 *	cc command line will result in the same set of predefined symbols.
 *	This is rather ugly and leaves a hole for future problems if we
 *	get out of sync with respect to what names the compiler predefines.
 */
static int pass1(op)
register struct op *op;
{
    char tmpFile[MAXPATH];
    int status;
    int n;
    void **els;

    DBUG_ENTER("pass1");

    dstr_Clear(cmdLine);

    dstr_Append(cmdLine,locate((options_IsSet(OPT_BIGLC1) ? "lc1b" : "lc1"),binDirs));

    if(options_IsSet(OPT_DEBUG)){
	dstr_Addf(cmdLine," -d%d",debugLevel);
	if(options_IsSet(OPT_OPTIMIZE)){
	    warning("-g incompatible with -O, -O turned off");
	    options_Clear(OPT_OPTIMIZE);
	}
    }

    if(options_IsSet(OPT_FFP))
	dstr_Addf(cmdLine," -ff");
    else if(options_IsSet(OPT_881))
	dstr_Addf(cmdLine," -f8");
    else if(options_IsSet(OPT_IEEE))
	dstr_Addf(cmdLine," -fi");
    else
	dstr_Addf(cmdLine," -fl");

    if(options_IsSet(OPT_SHORTINTS))
	dstr_Addf(cmdLine," -w");
    if(options_IsSet(OPT_LONGALIGN))
	dstr_Addf(cmdLine," -l");

    if(options_IsSet(OPT_020))
	dstr_Addf(cmdLine," -m2");
    else if(options_IsSet(OPT_030))
	dstr_Addf(cmdLine," -m3");

    if(options_IsSet(OPT_CATCH) && !options_IsSet(OPT_ABSDATA)){
	warning("+catch implies +abs-data");
	options_Set(OPT_ABSDATA);
    }

    dstr_Addf(cmdLine," -b%d",options_IsSet(OPT_ABSDATA) ? 0 : 1);
    dstr_Addf(cmdLine," -r%d",options_IsSet(OPT_ABSCODE) ? 0 : 1);

    if(options_IsSet(OPT_REGARGS))
	dstr_Addf(cmdLine,"r"); /* part of -r switch */

    {
	char buf[15], *p=buf;

	if(options_IsSet(OPT_ANSI))		*p++='a';
	if(options_IsSet(OPT_CPP))		*p++='+';
	if(options_IsSet(OPT_TRAD))		*p++='l', *p++='o';
	if(options_IsSet(OPT_REGARGS))		*p++='r';
	if(options_IsSet(OPT_PURESTRINGS))	*p++='s';
	
	if(p>buf){
	    *p='\0';
	    dstr_Addf(cmdLine," -c%s",buf);
	}
    }

    if(options_IsSet(OPT_COMPILE)){
	strcpy(tmpFile,tempDir);
	strcat(tmpFile,op->basename);
	strcat(tmpFile,".q");
	dstr_Addf(cmdLine," -o%s",tmpFile);
    }else{
	strcpy(tmpFile,op->basename);
	strcat(tmpFile,".pp");

	dstr_Addf(cmdLine," -o%s -p",tmpFile);
    }

    for(n=list_GetLen(undefines),els=list_GetEls(undefines); n>0; n--,els++){
	/*************************
	dstr_Addf(cmdLine," -u%s",(char *)*els);
	**************************/
	warning("-U%s ignored! (unimplemented)",(char *)*els);
    }

    addfList(cmdLine," -d%s",defines);
    addfList(cmdLine," %s",pass1PassThrus);

    for(n=list_GetLen(incDirs),els=list_GetEls(incDirs); n>0; n--,els++){
	char *id=(*els);
	int len=strlen(id);

	dstr_Addf(cmdLine," -i%s",*els);

	if(len>0 && id[len-1]!=':' && id[len-1]!='/')
	    dstr_Addf(cmdLine,"/");
    }

    dstr_Addf(cmdLine," %s",op->rootname);

    status=runCmd(cmdLine);
    if(status==0 && options_IsSet(OPT_EXEC) && !readable(tmpFile))
	status=1;

    DBUG_RETURN(int,status);
}

/* run the optimizer */
static int optimize(op)
struct op *op;
{
    DBUG_ENTER("optimize");

    dstr_Clear(cmdLine);
    dstr_Addf(cmdLine,"%s %s%s",locate("go",binDirs),tempDir,op->basename);

    addfList(cmdLine," %s",optPassThrus);

    DBUG_RETURN(int,runCmd(cmdLine));
}

/*
 *	Run second pass of compiler on a single operand.
 */
static int pass2(op)
struct op *op;
{
    char objFile[MAXPATH];
    int status;

#ifdef DBUG
    fprintf(stderr,"Entering pass2...\n");
#endif

    DBUG_ENTER("pass2");

    dstr_Clear(cmdLine);

    dstr_Append(cmdLine,locate("lc2",binDirs));

    if(!options_IsSet(OPT_STACKCHECK))
	dstr_Addf(cmdLine," -v");

    strcpy(objFile,op->basename);
    strcat(objFile,".o");

    dstr_Addf(cmdLine," -o%s",objFile);

    dstr_Addf(cmdLine," %s%s",tempDir,op->basename);

    addfList(cmdLine," %s",pass2PassThrus);

    status=runCmd(cmdLine);
    if(status==0 && options_IsSet(OPT_EXEC) && !readable(objFile))
	status=1;

    DBUG_RETURN(int,status);
}

/*
 *	I have not yet had occasion to use the macro assembler,so this
 *	part is not yet implemented.  If anyone wants to send me the
 *	appropriate code, I will be glad to install it.
 */
static int assemble(op)
struct op *op;
{
    DBUG_ENTER("assemble");
    warning("assembly pass not yet implemented");
    DBUG_RETURN(int,1);
}

static void filteredOutputFrom(source,filter)
FILE *source;
char **filter;
{
    char buf[200];

    DBUG_ENTER("FilteredOutputFrom");

    while(!feof(source)){
	char **ignore;

	fgets(buf,sizeof(buf),source);

	for(ignore=filter; *ignore!=NULL; ignore++)
	    if(strncmp(*ignore,buf,strlen(*ignore))==0)
	        break;

	if(*ignore==NULL)
	    fputs(buf,stdout);
    }

    DBUG_VOID_RETURN;
}

#ifdef AMIGA

static int execToFile(cmd,sink)
char *cmd;
FILE *sink;
{
    /* this is a dopey version that uses Execute, since I can't get fork to work */
    long fh;

    DBUG_ENTER("execToFile");

    if(sink==NULL)
	fh=0;
    else
	fh=chkufb(fileno(sink))->ufbfh;

    DBUG_RETURN(int,Execute(cmd,0L,fh)!=(-1));

#if 0
    char *argv[50],**argp=argv;
    struct ProcID child;
    static struct FORKENV env={0,0,0,0,0,NULL};
    int i;

    DBUG_ENTER("execToFile");

    if(cmd==NULL)
    	DBUG_RETURN(int,1);

    /* this is sort of stupid, cause forkv will just put them back together,
     * but oh well...
     */
    *argp=strtok(cmd," \t");
    while(*argp!=NULL)
    	*++argp=strtok(NULL," \t");

    if(sink==NULL)
        env.std_out=0;
    else{
    	struct UFB *ufb=chkufb(fileno(sink));
	int i;

	printf("argv[%d]:",argp-argv);
	for(argp=argv;*argp!=NULL;argp++)
	    printf(" %s",*argp);
	putchar('\n');

	printf("env: %d, %d, %d, %d, %d, %d\n",
		env.priority, env.stack, env.std_in, env.std_out,
		env.console, env.msgport);

	printf("ufb==0x%x, fh==0x%x, go? ",ufb,ufb->ufbfh);

	fflush(stdout);

	env.std_out=ufb->ufbfh; /* magic to get amigados handle from FILE */
    }

    if((i=forkv(argv[0],argv,&env,&child))==-1){
        poserr("forkv");
    	DBUG_RETURN(int,1);
    }

    printf("Fork returns: %d\n",i);

    DBUG_RETURN(int,wait(&child));
#endif
}

#endif
 
static int runCmd(cmd)
struct dstr *cmd;
{
    int status;
    char *buf=dstr_GetBuf(cmd);
    
    DBUG_ENTER("RunCommand");

    DBUG_3("cmd","execute '%s'",buf);
    if(options_IsSet(OPT_ECHO)){
	puts(buf);
	fflush(stdout);
    }

    CHECK_ABORT;

    if(options_IsSet(OPT_EXEC)){
	FILE *sink;

	if(sinkFile!=NULL && options_IsSet(OPT_FILTER)){
	    sink=fopen(sinkFile,"w+");
	    if(sink==NULL)
		perror(sinkFile);
	}else
	    sink=NULL;

	status=execToFile(buf,sink);
	DBUG_3("sys","subcommand returns status %d",status);

	if(sink!=NULL){
	    rewind(sink);
	    filteredOutputFrom(sink,filteredPrefixes);
	    fclose(sink);
	    unlink(sinkFile);
	}

	DBUG_RETURN(int,status);
    }else
	DBUG_RETURN(int,0);
}

/*
 *	Look through the list of paths pointed to by "vec" until we find
 *	a file with name given pointed to by "namep".  If none is found,
 *	the name pointed to by namep is returned.
 */
static char *locate(name,paths)
char *name;
struct list *paths;
{
    static char namebuf[MAXPATH];
    void **els;
    int n;
    
    DBUG_ENTER("locate");

    if(strchr(name,':')!=NULL || *name=='/')
	return name;	/* absolute */
	
    for(n=list_GetLen(paths),els=list_GetEls(paths); n>0; n--,els++){
	int len=strlen((char *)*els);

	strcpy(namebuf,(char *)*els);
	if(len>0 && namebuf[len-1]!=':' && namebuf[len-1]!='/')
	    strcat(namebuf,"/");
	strcat(namebuf,name);

	DBUG_3("try","look for '%s'",namebuf);
	if(readable(namebuf)){
	    name=namebuf;
	    break;
	}
    }

    DBUG_RETURN(char *,name);
}

/*
 *	Check to see if the file exists and is readable.
 */
static int readable(name)
char *name;
{
    register int status=0;
    register long fildes;
    
    DBUG_ENTER("readable");

#ifdef unix
    fildes=open(name,O_RDONLY);
    if(fildes >= 0){
	(void) close(fildes);
	status=1;
    }
#else
    fildes=Lock(name,ACCESS_READ);
    if(fildes != 0){
    	UnLock(fildes);
	status=1;
    }
#endif

    DBUG_RETURN(int,status);
}

/*
 *	If an executable is made from a single C file, the normal behavior
 *	for the unix environment is to treat the .o file as an intermediate
 *	file and remove it, so we follow suit.
 */
static void cleanObjects(mustExist)
int mustExist;
{
    int n;
    void **els;

    DBUG_ENTER("cleanObjects");
	
    for(n=list_GetLen(ops),els=list_GetEls(ops); n>0; n--,els++){
	char buf[MAXPATH];
        struct op *op=(struct op *)*els;

	if(!op_IsO(op)){
	    strcpy(buf,op->basename);
	    strcat(buf,".o");
	    if(unlink(buf)!=0 && mustExist)
	        warning("can't delete '%s'",buf);
        }
    }

    DBUG_VOID_RETURN;
}
