/*
 * Copyright (C) 1992 by Rush Record (rhr@clio.rice.edu)
 * 
 * This file is part of ED.
 * 
 * ED is free software; you can redistribute it and/or modify it under the terms
 * of the GNU General Public License as published by the Free Software Foundation.
 * 
 * ED is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
 * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
 * PARTICULAR PURPOSE.  See the GNU General Public License for more details.
 * 
 * You should have received a copy of the GNU General Public License along with ED
 * (see the file COPYING).  If not, write to the Free Software Foundation, 675
 * Mass Ave, Cambridge, MA 02139, USA.
 */
#include "opsys.h"

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

#include "rec.h"
#include "window.h"
#include "ed_dec.h"
#include "buffer.h"
#include "buf_dec.h"
#include "ctyp_dec.h"

extern Char *filename();

static Char edityet = 0;
static Char abortbuffer = 0;
static Char specialskip = 0;	/* flags that no prompting must be done when files run out */
static struct inhibit_flags
{
	unsigned int search:1;
	unsigned int paste:1;
	unsigned int line:1;
	unsigned int word:1;
	unsigned int chr:1;
} inhibit;

/******************************************************************************\
|Routine: insstr
|Callby: parse_long_option parse_option
|Purpose: Does what strstr() does, only case-insensitively.
|Arguments:
|    buf is the command line (which may contain upcase chars).
|    str is the string we're looking for (which must be all lowercase).
\******************************************************************************/
Char *insstr(buf,str)
Char *buf,*str;
{
	Char *p,*q,*r,c,d;
	
	for(p = buf,c = *str;(d = tolower(*p++));)
		if(c == d)
		{
			for(q = p,r = str + 1;(d = *r++);)
				if(((Char)tolower(*q++)) != d)
					break;
			if(!d)
				return(--p);
		}
	return(NULL);
}
	
/******************************************************************************\
|Routine: parse_option
|Callby: parse_options
|Purpose: Parses a simple option of the form -<character>.
|         Returns a cleaned-up buffer, and 1 if -char is present, else 0.
|Arguments:
|    buf is the command line.
|    chr is the <character> we're looking for.
|    exact indicates (if nonzero) that the case is not to be ignored.
\******************************************************************************/
Int parse_option(buf,chr,exact)
Char *buf;
Int chr,exact;
{
	static Char str[5] = {' ','-',' ',' ',0};
	static Char *localbuf = NULL;
	static Int localbufsiz;
	
	Char *from,*to;
	Int i,l;
	
	if((l = strlen(buf) + 3) > localbufsiz)
	{
		if(localbuf)
			ifree(localbuf);
		localbuf = (Char *)imalloc((localbufsiz = l));
	}
	str[2] = chr;
	strcpy(localbuf," ");
	strcat(localbuf,buf);
	strcat(localbuf," ");
	if(exact)
	{
		if(!(to = strstr(localbuf,str)))
			return(0);
	}
	else
		if(!(to = insstr(localbuf,str)))
			return(0);
	for(from = to + 3;(*to++ = *from++););
	for(from = localbuf + 1,to = localbuf;(*to++ = *from++););	/* eliminate the preceeding space */
	if((i = strlen(localbuf)) > 1)
		localbuf[i - 1] = '\0';	/* trim trailing space */
	strcpy(buf,localbuf);
	return(1);
}

/******************************************************************************\
|Routine: parse_long_option
|Callby: parse_options
|Purpose: Parses an option of the form -<character><string>.
|         Returns a cleaned-up buffer, and 1 if option is present, else 0.
|Arguments:
|    buf is the command line.
|    chr is the <character> we're looking for.
|    val is the returned <string>.
\******************************************************************************/
Int parse_long_option(buf,chr,val)
Char *buf;
Int chr;
Char *val;
{
	static Char str[4] = {' ','-',' ',0};
	static Char *localbuf = NULL;
	static Int localbufsiz;
	
	Char *from,*to,*p,c;
	Int i,l;
	
	if((l = strlen(buf) + 3) > localbufsiz)
	{
		if(localbuf)
			ifree(localbuf);
		localbuf = (Char *)imalloc((localbufsiz = l));
	}
	str[2] = chr;
	strcpy(localbuf," ");
	strcat(localbuf,buf);
	strcat(localbuf," ");
	if(!(to = insstr(localbuf,str)))
		return(0);
	for(p = val,from = to + 3;!isspace(c = *from++);*p++ = c);	/* copy the trailing value (possibly NULL) */
	*p = '\0';
	for(from--;(*to++ = *from++););
	for(from = localbuf + 1,to = localbuf;(*to++ = *from++););	/* eliminate the preceeding space */
	if((i = strlen(localbuf)) > 1)
		localbuf[i - 1] = '\0';	/* trim trailing space */
	strcpy(buf,localbuf);
	return(1);
}

/******************************************************************************\
|Routine: parse_options
|Callby: parse_filename
|Purpose: Parses options out of the command line. Sets appropriate flags.
|         Returns a cleaned-up buffer.
|Arguments:
|    file is the command line.
|    recover is the recovery flag, set if -r is present.
|    recfile is the journal file name (defaulted if absent).
|    process is the text-processing flag, set if -t is present.
|    multiple is the multiple-file-mode flag, set if -m is present.
|    binary is the binary-mode flag, set to 1 if -b is present, 2 if -h.
|    news is the flag that -n was present.
\******************************************************************************/
void parse_options(file,recover,recfile,process,multiple,binary,news)
Char *file,*recover,*recfile,*process,*multiple,*binary,*news;
{
	Char *local;
	Char *p;
	Char buf[1024],buf2[1024];
	Int l;
	
/* make a local copy with blank padding on both ends */
	strcpy((local = (Char *)imalloc((l = strlen(file)) + 3)) + 1,file);
	local[0] = ' ';
	local[l + 1] = ' ';
	local[l + 2] = '\0';
/* check for -n, which overrides everything */
#ifndef NO_NEWS
	if(parse_option(local,'n',1))
	{
		*news = 1;
		goto trim_servername;
	}
	else if(parse_option(local,'N',1))
	{
		*news = 2;
trim_servername:
		strcpy(file,local + 1);
		if((l = strlen(file)))
			file[l - 1] = '\0';
		return;
	}
#endif
	*news = 0;
/* turn -t into -r and set process flag */
	if((p = insstr(local," -t ")))
	{
		*process = 1;
		*(p + 2) = 'r';
	}
	else if((p = insstr(local," -t")))	/* NOTE! " -t" is not the same as " -t " */
	{
		*process = 1;
		*(p + 2) = 'r';
	}
	if(parse_option(local,'u',0))
		REPORTSTATUS = 1;
	if(parse_long_option(local,'k',buf))
		set_output_bak((strlen(buf))? buf : NULL);
	if(parse_option(local,'m',0))
		*multiple = 1;
	if(parse_option(local,'b',0))
		*binary = 1;
	if(parse_option(local,'h',0))	/* -h takes precedence if both -b and -h are present */
		*binary = 2;
	if(parse_long_option(local,'f',buf))
	{
		strcpy(buf2,"-f");
		strcat(buf2,buf);
		file_list_init(buf2);
	}
	if(parse_long_option(local,'r',buf))
	{
		*recover = 1;
		strcpy(recfile,(strlen(buf))? buf : (Char *)JOURNAL_FILE);
	}
	if(parse_long_option(local,'t',buf))
	{
		*process = 1;
		strcpy(recfile,(strlen(buf))? buf : (Char *)JOURNAL_FILE);
	}
	if(parse_long_option(local,'i',buf))
	{
		my_sscanf(buf,"%d",&l);
		if(l < 1)
			l = 1;
		edit_init_pos(l);
	}
	if(parse_long_option(local,'s',buf))
	{
		string_to_buf(buf,&SEARCHBUF);
		inhibit.search = 1;
	}
	if(parse_long_option(local,'p',buf))
	{
		string_to_buf(buf,&KILLBUF);
		inhibit.paste = 1;
	}
	if(parse_long_option(local,'l',buf))
	{
		string_to_buf(buf,&LINEBUF);
		inhibit.line = 1;
	}
	if(parse_long_option(local,'w',buf))
	{
		string_to_buf(buf,&WORDBUF);
		inhibit.word = 1;
	}
	if(parse_long_option(local,'c',buf))
	{
		string_to_buf(buf,&CHARBUF);
		inhibit.chr = 1;
	}
	strcpy(file,local + 1);
	if((l = strlen(file)))
		file[l - 1] = '\0';
}

/******************************************************************************\
|Routine: parse_skip
|Callby: command edit
|Purpose: Flags that parse_fnm() must not prompt when files run out.
|Arguments:
|    flag is 0 or 1.
\******************************************************************************/
void parse_skip(flag)
Int flag;
{
	specialskip = flag;
}

/******************************************************************************\
|Routine: strip_quotes
|Callby: command init_term journal load_file main match_search parse_fnm wincom
|Purpose: Strips quotes from quoted file names (in place).
|Arguments:
|    file is the file name.
\******************************************************************************/
void strip_quotes(file)
Char *file;
{
	register Int i;
	register Char *from,*to;
	
	from = to = file;
	if(*from == '"')
	{
		i = strlen(from) - 1;
		if(*(from + i) == '"' && *(from + i - 1) != '\\')
		{
			from = to + 1;
			while(--i)
				if(*from == '\\' && *(from + 1) == '"')
				{
					*to++ = '"';
					from += 2;
					i--;	/* for the \ character */
				}
				else
					*to++ = *from++;
			*to = '\0';
		}
	}
}

/******************************************************************************\
|Routine: parse_filename
|Callby: edit main parse_fnm
|Purpose: Handles parsing of command line options and file names
|Arguments:
|    command is the command line buffer. this is returned in a modified form.
|    commlen is the length of the command buffer.
|    editfile is the returned name of the file to be edited.
|    recover is a flag indicating that we are in recovery mode, so edits come from the journal file.
|    recfile is the returned name of the journal file from which edits are to be recovered.
|    process is a flag indicating that we are in text processing mode, and must not prompt user.
|    multiple is a flag indicating that -m was specified on the command line.
|    binary is a flag indicating that -b or -h was specified on the command line.
|    news is a flag indicating that -n was specified.
\******************************************************************************/
Int parse_filename(command,commlen,editfile,recover,recfile,process,multiple,binary,news)
Char *command,*editfile,*recover,*recfile,*process,*multiple,*binary,*news;
Int commlen;
{
	Char nextfile[512];

/* parse command line options */
redo:
/* handle ABORT command */
	if(abortbuffer)	/* note, file_list_next will fail because command calls file_list_abort */
	{
		abortbuffer = 0;	/* make it a one-shot deal */
		command[0] = '\0';	/* terminate parsing from buffer */
	}
	parse_options(command,recover,recfile,process,multiple,binary,news);
	if(*news)
	{
		strcpy(editfile,command);
		abortbuffer = 1;
		edityet = 1;
		return(1);
	}
/* see if more files are coming from list or wildcard */
filelist:
	if(file_list_next(nextfile))
	{
		parse_options(nextfile,recover,recfile,process,multiple,binary,news);	/* we allow options in file lists, alas */
		strcpy(editfile,nextfile);
		if(*recover)
			unjournal_init(recfile,nextfile,&inhibit);	/* dump the file name that is in the journal file */
		if(!edityet)
		{
			edityet = 1;
			if(*process)
			{
				putpurge();
				putoff();
			}
		}
		return(1);
	}
/* see if more tokens are waiting in user buffer */
	if(get_token(command,nextfile))
	{
		strip_quotes(nextfile);
		file_list_init(nextfile);
		goto filelist;
	}
/* nothing on command line, prompt if appropriate */
	if(*process)
		cleanup(0);
	if(*recover)	/* they specified -r but no file, use file specified in journal file */
	{
		unjournal_init(recfile,editfile,&inhibit);	/* use the file name that is in the journal file */
		return(1);
	}
	if(specialskip)
	{
		specialskip = 0;
		return(0);
	}
/* prompt them for a file name */
	*process = *recover = *multiple = *binary = 0;
	inhibit.search = inhibit.paste = inhibit.line = inhibit.word = inhibit.chr = 0;
	marge(1,NROW);
	move(NROW,1);
	if(edityet)
		next();
	else
		edityet = 1;
	BOTROW = NROW;
	if(inquire("File",command,commlen,1))
	{
#ifndef VMS
		next();
#endif
		goto redo;
	}
	ers_end();
	vtend();
	return(0);
}

/******************************************************************************\
|Routine: abort_parsing
|Callby: command
|Purpose: Aborts the taking of multiple filenames from the command line.
|Arguments:
|    none
\******************************************************************************/
void abort_parsing()
{
	abortbuffer = 1;
}

