/*
 * Entry point, initialization, miscellaneous routines.
 */

#ifdef AMIGA
/* Compile with -HPreHeader.q to get "less.h"! */
#else
#include "less.h"
#endif

#include "position.h"
#include <setjmp.h>
#include <signal.h>

public int      ispipe;
public jmp_buf  main_loop;
public char *   first_cmd;
public char *   every_first_cmd;
public int      new_file;
public int      is_tty;
public char     current_file[FILENAME];
public char     previous_file[FILENAME];
public POSITION prev_pos;
public int      any_display;
public int      ac;
public char **  av;
public int      curr_ac;
#if LOGFILE
public int      logfile = -1;
public int      force_logfile = 0;
public char *   namelogfile = NULL;
#endif
#if EDITOR
public char *   editor;
#endif

extern int file;
extern int nbufs;
extern int sigs;
extern int quit_at_eof;
extern int p_nbufs, f_nbufs;
extern int back_scroll;
extern int top_scroll;
extern int sc_height;
extern int errmsgs;

/* Prototypes for functions defined in main.c */

static void SeekRoot __PROTO((ULONG lock));


#ifdef AMIGA
/********** amiga **************/
#include <ctype.h>
#include <dos.h>
#include <intuition/intuition.h>
#include <exec/memory.h>
#include <ios1.h>

extern struct UFB _ufbs[];

/* Used by SeekRoot */
#define MAXPATHSTRING 256
static char    absDir[MAXPATHSTRING];

struct IntuitionBase *IntuitionBase;
struct Remember *RememberKey;
int called_from_WB = 0;


/* max items and chars that *.c can expand to */
#define MAXTEMPLATES 100
public char     *local_argv[MAXTEMPLATES];
public int      local_argc;
#endif

/*
 * Edit a new file.
 * Filename "-" means standard input.
 * No filename means the "current" file, from the command line.
 */
#ifdef __STDC__
void edit (register char *filename)
#else
        public void
edit(filename)
        register char *filename;
#endif
{
        register int f;
        register char *m;
        POSITION initial_pos;
        char message[100];
        char tempfile[FILENAME];
        static int didpipe;

        initial_pos = NULL_POSITION;
        if (filename == NULL || *filename == '\0')
        {
                if (curr_ac >= ac)
                {
                        error("No current file");
                        return;
                }
                filename = av[curr_ac];
        }
        if (strcmp(filename, "#") == 0)
        {
                if (*previous_file == '\0')
                {
                        error("no previous file");
                        return;
                }
                strtcpy(tempfile, previous_file, sizeof(tempfile));
                filename = tempfile;
                initial_pos = prev_pos;
        }
        if (strcmp(filename, "-") == 0)
        {
                /*
                 * Use standard input.
                 */
                if (didpipe)
                {
                        error("Can view standard input only once");
                        return;
                }
                f = 0;
#ifdef AMIGA
                /* get standard input */
                if ( !chkufb(0) ) /* if linked with tinymain, we get no stdin */
                {
                    _ufbs[0].ufbfh = Input();
                    _ufbs[0].ufbflg |= UFB_RA | O_RAW;
                    if ( !called_from_WB ) _ufbs[0].ufbflg |= UFB_NC;
                }
#endif
        } else if ((m = bad_file(filename, message, sizeof(message))) != NULL)
        {
                error(m);
                return;
        } else if ((f = open(filename, 0)) < 0)
        {
                error(errno_message(filename, message, sizeof(message)));
                return;
        }

#ifdef AMIGA
/* SAS 5.10a isatty is broken; reports that pipe:x is a tty */
        if (isatty(f) && f==0)
#else
        if (isatty(f))
#endif
        {
                /*
                 * Not really necessary to call this an error,
                 * but if the control terminal (for commands)
                 * and the input file (for data) are the same,
                 * we get weird results at best.
                 */
                error("Can't take input from a terminal");
                if (f > 0)
                        close(f);
                return;
        }

#if LOGFILE
        /*
         * If he asked for a log file and we have opened standard input,
         * create the log file.
         * We take care not to blindly overwrite an existing file.
         */
        end_logfile();
        if (f == 0 && namelogfile != NULL && is_tty)
        {
                int exists;
                int answer;

                /*
                 * {{ We could use access() here. }}
                 */
                exists = open(namelogfile, 0);
                close(exists);
                exists = (exists >= 0);

                if (exists && !force_logfile)
                {
                        static char w[] = "WARNING: log file exists: ";
                        strcpy(message, w);
                        strtcpy(message+sizeof(w)-1, namelogfile,
                                sizeof(message)-sizeof(w));
                        error(message);
                        answer = 'X';   /* Ask the user what to do */
                } else
                        answer = 'O';   /* Create the log file */

        loop:
                switch (answer)
                {
                case 'O': case 'o':
                        logfile = creat(namelogfile, 0644);
                        break;
                case 'A': case 'a':
                        logfile = open(namelogfile, 1);
                        if (lseek(logfile, (offset_t)0, 2) < 0)
                        {
                                close(logfile);
                                logfile = -1;
                        }
                        break;
                case 'D': case 'd':
                        answer = 0;     /* Don't print an error message */
                        break;
                case 'q':
                        quit();
                default:
                        putstr("\n  Overwrite, Append, or Don't log? ");
                        answer = getchr();
                        putstr("\n");
                        flush();
                        goto loop;
                }

                if (logfile < 0 && answer != 0)
                {
                        sprintf(message, "Cannot write to \"%s\"",
                                namelogfile);
                        error(message);
                }
        }
#endif

        /*
         * We are now committed to using the new file.
         * Close the current input file and set up to use the new one.
         */
        if (file > 0)
                close(file);
        new_file = 1;
        strtcpy(previous_file, current_file, sizeof(previous_file));
        strtcpy(current_file, filename, sizeof(current_file));
        prev_pos = position(TOP);
#ifdef AMIGA
        /* Some heuristics to determine if we've been given a pipe.
           (there must be a better way...)
        */
        ispipe = ((f == 0) && !called_from_WB)
            || (strnicmp(filename, "pipe:", 5) == 0); /* kludge! */
#else
        ispipe = (f == 0);
#endif
        if (ispipe)
                didpipe = 1;
        file = f;
        ch_init( (ispipe) ? p_nbufs : f_nbufs );
        init_mark();

        if (every_first_cmd != NULL)
                first_cmd = every_first_cmd;

        if (is_tty)
        {
                int no_display = !any_display;
                any_display = 1;
                if (no_display && errmsgs > 0)
                {
                        /*
                         * We displayed some messages on error output
                         * (file descriptor 2; see error() function).
                         * Before erasing the screen contents,
                         * display the file name and wait for a keystroke.
                         */
                        error(filename);
                }
                /*
                 * Indicate there is nothing displayed yet.
                 */
                pos_clear();
                if (initial_pos != NULL_POSITION)
                        jump_loc(initial_pos);
        }
}

/*
 * Edit the next file in the command line list.
 */
#ifdef __STDC__
void next_file (int n)
#else
        public void
next_file(n)
        int n;
#endif
{
        if (curr_ac + n >= ac)
        {
                if (quit_at_eof)
                        quit();
                error("No (N-th) next file");
        } else
                edit(av[curr_ac += n]);
}

/*
 * Edit the previous file in the command line list.
 */
#ifdef __STDC__
void prev_file (int n)
#else
        public void
prev_file(n)
        int n;
#endif
{
        if (curr_ac - n < 0)
                error("No (N-th) previous file");
        else
                edit(av[curr_ac -= n]);
}

#ifndef AMIGA
/*
 * Copy a file directly to standard output.
 * Used if standard output is not a tty.
 */
        static void
cat_file()
{
        register int c;

        while ((c = ch_forw_get()) != EOF)
                putchr(c);
        flush();
}
#endif


#ifdef AMIGA
/**************** amiga *****************************/
/* Bob Leivian  4/28/87 fudge up things so it will work
   when called from Work Bench */

char argvbuf[80];

#include "workbench/startup.h"

#ifdef MANX
/* ignore AZTECs wb stuff */
_wb_parse(ignore, ignore2)
char *ignore;
char *ignore2;
{
    return;
}
#endif
#endif


#ifdef NO_GETENV
/* this requires the workbench disk --
  ignore all environment variables for now */
char * getenv(ignore)
{
        return NULL;
}
#endif

/*
 * Entry point.
 */
#ifdef __STDC__
int main (int argc, char **argv)
#else
main(argc, argv)
        int argc;
        char *argv[];
#endif
{
        char *getenv();


#ifdef AMIGA
/***************** amiga ********************/
        IntuitionBase = OpenLibrary ( "intuition.library", 0 );
        RememberKey = NULL;
        /* if we were called from the workbench we will have no args
           but a pointer to WBstruct, get the filename from this structure */
        if(argc == 0) {
                struct WBStartup *WBmsg;
                struct WBArg *p;
                char *cp, c;
                BPTR newlock;

                /* the argv is really the work bench structure */
                WBmsg = (struct WBStartup *) argv;
                p = WBmsg->sm_ArgList;

                /* fake up the args now */
                /* argv[0] = p->wa_Name; */
                p++; /* ignore first parm (name), since Less don't use it */
                for ( local_argc = 1; local_argc < WBmsg->sm_NumArgs
                    && local_argc < MAXTEMPLATES; local_argc++ )
                {
                    *absDir = '\0';
                    /* SeekRoot UnLocks its argument */
                    newlock = DupLock ( p->wa_Lock );
                    SeekRoot ( newlock );
                    /* The first part is really a device name */
                    for ( cp=absDir; (c = *cp) && c != '/'; cp++ )
                        /* nothing */;
                    *cp = ':';
                    if ( c == '\0' ) /* root dir of a device */
                        *++cp = '\0';
                    else
                        strcat(cp, "/");
                    if ( !(cp = AllocRemember(&RememberKey,
                        strlen(absDir)+strlen(p->wa_Name)+1, 0L)) )
                        quit();
                    strcpy ( cp, absDir );
                    local_argv[local_argc] = strcat(cp, p->wa_Name);
                    p++;
                }
                local_argv[local_argc] = NULL;

                called_from_WB = 1;
        }


#endif

        /*
         * Process command line arguments and LESS environment arguments.
         * Command line arguments override environment arguments.
         */
        init_option();
        scan_option(getenv("LESS"));
        argv++;
        while ( (--argc > 0) &&
                (argv[0][0] == '-' || argv[0][0] == '+') &&
                 argv[0][1] != '\0')
                scan_option(*argv++);

#if EDITOR
        editor = getenv("EDITOR");
        if (editor == NULL || *editor == '\0')
                editor = "ed";
#endif

        /*
         * Set up list of files to be examined.
         */
#ifdef AMIGA
        if ( called_from_WB )
        {
            ac = local_argc-1;
            av = local_argv+1; /* CLI case did an argv++ */
        }
        else
        {
#endif
        ac = argc;
        av = argv;
#ifdef AMIGA
        }
#endif


        curr_ac = 0;

        /*
         * Set up terminal, etc.
         */
#ifdef AMIGA
        is_tty = 1;
        ttopen();
#else
        is_tty = isatty(1);
        if (!is_tty)
        {
                /*
                 * Output is not a tty.
                 * Just copy the input file(s) to output.
                 */
                if (ac < 1)
                {
                        edit("-");
                        cat_file();
                } else
                {
                        do
                        {
                                edit((char *)NULL);
                                if (file >= 0)
                                        cat_file();
                        } while (++curr_ac < ac);
                }
                quit();
        }
#endif

        raw_mode(1);
        get_term();
        open_getchr();
        init();

        if (setjmp(main_loop))
                quit();
        init_signals();

        /*
         * Select the first file to examine.
         */
        if (ac < 1)
                edit("-");      /* Standard input */
        else
        {
                /*
                 * Try all the files named as command arguments.
                 * We are simply looking for one which can be
                 * opened without error.
                 */
                do
                {
                        edit((char *)NULL);
                } while (file < 0 && ++curr_ac < ac);
        }

        if (file >= 0)
                commands();
        quit();
        /*NOTREACHED*/
}

/*
 * Copy a string, truncating to the specified length if necessary.
 * Unlike strncpy(), the resulting string is guaranteed to be null-terminated.
 */
#ifdef __STDC__
void strtcpy (char *to, char *from, int len)
#else
strtcpy(to, from, len)
        char *to;
        char *from;
        int len;
#endif
{
        strncpy(to, from, len);
        to[len-1] = '\0';
}

/*
 * Exit the program.
 */
#ifdef __STDC__
void quit (void)
#else
        public void
quit()
#endif
{
        /*
         * Put cursor at bottom left corner, clear the line,
         * reset the terminal modes, and exit.
         */
#if LOGFILE
        end_logfile();
#endif
#ifdef AMIGA
        ttclose();

        if (IntuitionBase)
        {
            FreeRemember( &RememberKey, 1 );
            CloseLibrary(IntuitionBase);
        }
#else
        lower_left();
        clear_eol();
        deinit();

        flush();
        raw_mode(0);

#endif

        exit(0);
}


#ifdef AMIGA

/* Thanks to Bruce Rogers for a UseNet posting including the following
   useful function...
*/

/*!*******************************************************************
 * FindRoot by Bruce Rogers 1/20/90
 *********************************************************************/
/*
------------------------------------------------------          Quantum _\/_
2727 Eel                   Bruce (6502 RULES!) Rogers        |\  Duck  ( 0 0)
Davis, Ca 95616            Quantum Duck Software,           |\ \______/ / \\\
916-756-2684               rogers@iris.ucdavis.edu         |\ <  <     |   \/
"My brain is on fire!"                                       \________/ Quark!

*/


/*!*******************************************************************
 * Recursively go up parent chain, looking for oldest parent.
 * Create the absolute path string as we go.
 *********************************************************************/
static void SeekRoot(lock)
ULONG   lock;
{
struct  FileInfoBlock   *fileInfo;
ULONG newlock;
char NameEnd[MAXPATHSTRING], sep;

    fileInfo=AllocMem(sizeof(struct FileInfoBlock),0);

    Examine(lock,fileInfo);
    strcpy ( NameEnd, absDir );
    strcpy ( absDir, fileInfo->fib_FileName );
    sep = 0; if ( *absDir ) sep = absDir[strlen(absDir) - 1];
    if ( sep && *NameEnd && sep != '/' && sep != ':' ) strcat ( absDir, "/" );
    strcat ( absDir, NameEnd );
    newlock = ParentDir(lock);
    UnLock(lock);
    if (newlock!=NULL) SeekRoot(newlock);

    FreeMem(fileInfo,sizeof(struct FileInfoBlock));
}
#endif
