/*
Auto:		sc <file>
*/

/* $Revision Header built automatically *************** (do not edit) ************
**
** © Copyright by GuntherSoft
**
** File             : SnakeSYS:CPrgs/Utils/Man.c
** Created on       : Friday, 16.07.93 18:00:23
** Created by       : Kai Iske
** Current revision : V1.7
**
**
** Purpose
** -------
**   - This is a man program, which may scan different directories
**     for man-pages. These directories are set within an ENV-VAR
**     called MANPATHS. Additionally a VIEWER may be set using
**     ENV-VAR MANVIEW (for plain ASCII) or MANVIEWAG (for
**     AMIGAGUIDE). THIS ONE`S PURE AND IN THE PUBLIC DOMAIN
**
** Revision V1.7
** --------------
** created on Friday, 31.12.93 14:00:28  by  Kai Iske.   LogMessage :
**   - All config variables have been moved to a single one,
**     which will be parsed just like a CommandLine
**     (Suggested by : Michael 'Mick' Hohmann)
**
** Revision V1.6
** --------------
** created on Thursday, 16.12.93 16:49:51  by  Kai Iske.   LogMessage :
**   - Added MANRMEXT, MANAGEXT and MANNOVIEW options,
**     which may be used for extensibility of MAN
**     (Somehow suggested by : Michael 'Mick' Hohmann)
**
** Revision V1.5
** --------------
** created on Thursday, 09.12.93 00:58:47  by  Kai Iske.   LogMessage :
**   - Referenced free memory area
**
** Revision V1.4
** --------------
** created on Wednesday, 08.12.93 22:07:47  by  Kai Iske.   LogMessage :
**  -*-  created on Wednesday, 08.12.93 22:07:47  by  Kai Iske.   LogMessage :
**   - DOS-Library wasn`t closed
**
** Revision V1.3
** --------------
** created on Wednesday, 08.12.93 19:26:17  by  Kai Iske.   LogMessage :
**  -*-  changed on Wednesday, 08.12.93 19:31:23  by  Kai Iske.   LogMessage :
**   - Added CTRL-C checking
**  -*-  created on Wednesday, 08.12.93 19:26:17  by  Kai Iske.   LogMessage :
**   - Recompiled using SAS 6.50
**   - Reduced stack usage
**   - Reduced executable size
**
** Revision V1.2
** --------------
** created on Tuesday, 27.07.93 15:20:51  by  Kai Iske.   LogMessage :
**   - Used MAN as a keyword for the template which prevented MAN
**     to accept inputs like "man man".
**
** Revision V1.1
** --------------
** created on Monday, 26.07.93 17:42:32  by  Kai Iske.   LogMessage :
**   - Accidentially called Exit() instead of exit(), which
**     prevented the program to pass the cleanup code of SAS.
**     So a lock to the directory was kept and the shell could
**     never been left.............
**
** Revision V1.0
** --------------
** created on Friday, 16.07.93 18:00:23  by  Kai Iske.   LogMessage :
**  -*-  changed on Saturday, 17.07.93 16:30:41  by  Kai Iske.   LogMessage :
**   - Man now searches for files that end up with .doc/.man/.guide
**     If a .guide file is found, the second Viewer (MANVIEWAG)
**     will be used to display the AmigaGuide viewer. Otherwise
**     the normal viewer (MANVIEW) will be used
**  -*-  created on Friday, 16.07.93 18:00:23  by  Kai Iske.   LogMessage :
**     --- Initial release ---
**
*********************************************************************************/
#define REVISION "1.7"
#define REVDATE  "31.12.93"
#define REVTIME  "14:00:28"
#define AUTHOR   "Kai Iske"
#define VERNUM   1
#define REVNUM   7

#include	<string.h>
#include	<stdlib.h>
#include	<exec/types.h>
#include	<exec/memory.h>
#include	<exec/execbase.h>
#include	<proto/exec.h>
#include	<proto/dos.h>
#include	<dos/dos.h>
#include	<dos/exall.h>
#include	<dos/dostags.h>





/**********************************************************************/
/*                             Prototypes                             */
/**********************************************************************/
static char	*GetDir(char *NewName, char *OldName);



/**********************************************************************/
/*                           Version-String                           */
/**********************************************************************/
static const char *Version	= "$VER: Man "REVISION" ("REVDATE")\0";


/**********************************************************************/
/*                 Template for Command-Line parsing                  */
/**********************************************************************/
static const char *Template	= "MANPAGE/A";
enum {MAN_ARG, TMP_LAST_ARG};


/**********************************************************************/
/*                    Template for MANOPT settings                    */
/**********************************************************************/
static const char *ManTemplate	= "MANPATHS/K/A,MANVIEW/K/A,MANVIEWAG/K/A,MANNRMEXT/K,MANAGEXT/K,MANNOVIEW/K";
enum {MANPATHS_ARG, MANVIEW_ARG, MANVIEWAG_ARG, MANNRMEXT_ARG, MANAGEXT_ARG, MANNOVIEW_ARG, MAN_LAST_ARG};




/**********************************************************************/
/*                       This is the main part                        */
/**********************************************************************/
ULONG __saveds main(void)
{
	struct	ExecBase	*SysBase;
	struct	DOSLibrary	*DOSBase;
	struct	Process		*MyProc;
	struct	RDArgs		*RDArgs,
				*ManRDArgs;
	struct	ExAllControl	*EAC;
	char	*MainBuffer	= NULL,
		*ManOpt,
		*ManPaths,
		*ViewCmd,
		*ViewCmdAG,
		*CheckDir,
		*SearchName,
		*FileName,
		*Pattern,
		*NormExt,
		*AGExt,
		*NonExt;
	APTR	*Args,
		*ManArgs;
	APTR	EAB;
	char	*DirOffset;
	BPTR	TestLock;
	BPTR	OutHandle;
	ULONG	MySig;
	BOOL	Found		= FALSE,
		Breaked		= FALSE,
		StartedSearch	= FALSE,
		GoOn;

		// Get Pointer to SysBase

	SysBase	= *((struct ExecBase **)0x4L);

		// Don`t start from WB

	MyProc	= (struct Process *)FindTask(NULL);

	if(!MyProc->pr_CLI)
	{
		struct	WBStartup	*Msg;

		WaitPort(&MyProc->pr_MsgPort);
		Msg	= (struct WBStartup *)GetMsg(&MyProc->pr_MsgPort);
		Disable();
		ReplyMsg((struct Message *)Msg);
		return(0);
	}

		// Try to open DOSBase

	if(!(DOSBase	= (struct DOSLibrary *)OpenLibrary("dos.library", 0)))
		return(20);

		// Get Out Handle

	OutHandle = Output();

		// Check for System we`re running on

	if(((struct Library *)SysBase)->lib_Version < 37)
	{
		Write(OutHandle, "You must use KickStart 2.04 (37.175) or higher for MAN\n", 55);
		CloseLibrary((struct Library *)DOSBase);
		return(20);
	}

		// Get buffer for Commandline parsing

	if((Args = AllocVec(TMP_LAST_ARG * sizeof(ULONG), MEMF_CLEAR)))
	{
			// Get buffer for MANOPT parsing

		if((ManArgs = AllocVec(MAN_LAST_ARG * sizeof(ULONG), MEMF_CLEAR)))
		{
				// Get RDArgs structure for MANOPT parsing

			if((ManRDArgs = AllocDosObject(DOS_RDARGS, NULL)))
			{
					// Get structure for ExAll()

				if((EAC = AllocDosObject(DOS_EXALLCONTROL, NULL)))
				{
						// Get buffer for ExAll()

					if((EAB = AllocVec(sizeof(struct ExAllData)*20, MEMF_CLEAR)))
					{
							// Try to parse commandline

						if((RDArgs = ReadArgs((char *)Template, (LONG *)Args, NULL)))
						{
								// Try to get buffer for Strings

							if((MainBuffer = AllocVec((1024 * 12), MEMF_CLEAR)))
							{
									// Set up pointers to buffers

								ManOpt		= MainBuffer;
								ManPaths	= (ManOpt + 2048);
								ViewCmd		= (ManPaths + 1024);
								ViewCmdAG	= (ViewCmd + 1024);
								CheckDir	= (ViewCmdAG + 1024);
								SearchName	= (CheckDir + 1024);
								FileName	= (SearchName + 1024);
								Pattern		= (FileName + 1024);
								NormExt		= (Pattern + 1024);
								AGExt		= (NormExt + 1024);
								NonExt		= (AGExt + 1024);

									// Rearrange pointer

								DirOffset	= ManPaths;

									// Try to get MANOPT Env-Variable

								if(GetVar("MANOPT", ManOpt, 2048, GVF_GLOBAL_ONLY) >= 1)
								{
										// Try to parse strings

									strcat(ManOpt, "\n");
									ManRDArgs->RDA_Source.CS_Buffer	= ManOpt;
									ManRDArgs->RDA_Source.CS_Length	= strlen(ManOpt);
									ManRDArgs->RDA_Source.CS_CurChr	= 0;
									ManRDArgs->RDA_DAList		= NULL;
									ManRDArgs->RDA_Buffer		= NULL;
									ManRDArgs->RDA_BufSiz		= 0;
									ManRDArgs->RDA_ExtHelp		= NULL;
									ManRDArgs->RDA_Flags		= 0;

										// Try to parse MANOPT variable

									if(ReadArgs((char *)ManTemplate, (LONG *)ManArgs, ManRDArgs))
									{
											// Get Paths etc.pp

										strcpy(ManPaths, (char *)ManArgs[MANPATHS_ARG]);
										strcpy(ViewCmd, (char *)ManArgs[MANVIEW_ARG]);
										strcpy(ViewCmdAG, (char *)ManArgs[MANVIEWAG_ARG]);

											// Per default use the ASCII viewer for "non-extension" files

										strcpy(NonExt, ViewCmd);

											// Get additional patterns for normal texts

										if(ManArgs[MANNRMEXT_ARG])
											strcpy(NormExt, (char *)ManArgs[MANNRMEXT_ARG]);

											// Get additional patterns for AmigaGuide texts

										if(ManArgs[MANAGEXT_ARG])
											strcpy(AGExt, (char *)ManArgs[MANAGEXT_ARG]);

											// Get nam of viewer to use when no extension was found

										if(ManArgs[MANNOVIEW_ARG])
											strcpy(NonExt, (char *)ManArgs[MANNOVIEW_ARG]);

											// Set pattern for ExAll() search

										strcpy(SearchName, Args[MAN_ARG]);

											// Append patterns

										strcat(SearchName, "(.doc|.man|.guide|");
										strcat(SearchName, NormExt);
										strcat(SearchName, "|");
										strcat(SearchName, AGExt);
										strcat(SearchName, "|)");

											// Parse the pattern

										if(ParsePatternNoCase(SearchName, Pattern, 1024) != -1)
										{

											StartedSearch	= TRUE;

												// Loop for all dirs and wait until file has been found

											while(!Found && !Breaked && DirOffset)
											{
												MySig	= CheckSignal(SIGBREAKF_CTRL_C);

												if(!(MySig & SIGBREAKF_CTRL_C))
												{
														// Extract next directory from list

													DirOffset = GetDir(CheckDir, DirOffset);

														// Add "/" if neither "/", nor ":" end the directory

													if((CheckDir[strlen(CheckDir) - 1] != '/') && (CheckDir[strlen(CheckDir) - 1] != ':'))
														strcat(CheckDir, "/");

														// Directory available ???

													if((TestLock = Lock(CheckDir, ACCESS_READ)))
													{
															// Fill in ExAll structure

														EAC->eac_LastKey	= 0;
														EAC->eac_MatchString	= Pattern;
														EAC->eac_MatchFunc	= NULL;

														do
														{
															MySig = CheckSignal(SIGBREAKF_CTRL_C);
															if((MySig & SIGBREAKF_CTRL_C))
																Breaked = TRUE;

																// Do the scanning

															GoOn = ExAll(TestLock, EAB, (20*sizeof(struct ExAllData)), ED_NAME, EAC);

																// Error occured ???

															if((!GoOn) && (IoErr() != ERROR_NO_MORE_ENTRIES))
																PrintFault(IoErr(), "Man ");

																// End of dir reached ;

															if(EAC->eac_Entries == 0)
																GoOn = FALSE;
															else if(!Breaked)
															{
																	// Get first name matching

																strcpy(FileName, ((struct ExAllData *)EAB)->ed_Name);

																	// Check extension to decide whether it`s an AmigaGuide file

																if(strchr(FileName, '.'))
																{
																	char	*ExtPtr	= AGExt,
																		*DelPtr;
																	int	ExtLen;
																	BOOL	FoundAG	= FALSE;

																		// Append default .guide extension and additional
																		// seperator for ease of calculating the length

																	strcat(ExtPtr, "|.guide|");

																		// Loop for all extensions

																	while(!FoundAG && (ExtPtr = strchr(ExtPtr, '.')))
																	{
																			// Calc len of extension

																		DelPtr	= strchr(ExtPtr, '|');
																		ExtLen	= DelPtr - ExtPtr;

																		if(!strnicmp(&FileName[strlen(FileName) - ExtLen], ExtPtr, ExtLen))
																			FoundAG	= TRUE;

																		ExtPtr++;
																	}

																		// Now set view command accordingly

																	if(!FoundAG)
																		strcpy(SearchName, ViewCmd);
																	else
																		strcpy(SearchName, ViewCmdAG);

																		// Set flag that we`ve found the file

																	Found = TRUE;
																}
																else
																{
																		// Only use default viewer for non-extension files, when not set to "none"

																	if(stricmp(NonExt, "none"))
																	{
																			// No extension use default viewer

																		strcpy(SearchName, NonExt);

																			// Set flag that the entry was found

																		Found = TRUE;
																	}
																}

																	// Append directory and FileName

																strcat(SearchName, " ");
																strcat(SearchName, CheckDir);

																	// Copy Filename to place where the last quote used to reside

																strcat(SearchName, FileName);
															}
														} while(GoOn);

															// Unlock Directory

														UnLock(TestLock);
													}
													else
													{
															// Display message

														FPuts(OutHandle, "Man : Skipping directory; not existent : ");
														FPuts(OutHandle, CheckDir);
														FPuts(OutHandle, "\n");
													}
												}
												else
													Breaked = TRUE;
											}

											if(!Found)
												strcpy(FileName, Args[MAN_ARG]);

										}
										else
											PrintFault(IoErr(), "Man ");

										FreeArgs(ManRDArgs);
									}
									else
										PrintFault(IoErr(), "Man (MANOPT) ");
								}
								else
									PrintFault(IoErr(), "Man (MANOPT) ");
							}
							else
								PrintFault(ERROR_NO_FREE_STORE, "Man ");

							FreeArgs(RDArgs);
						}
						else
							PrintFault(IoErr(), "Man ");

						FreeVec(EAB);
					}
					else
						FPuts(OutHandle, "Man : Could not allocate buffer for ExAll()\n");

					FreeDosObject(DOS_EXALLCONTROL, (void *)EAC);
				}
				else
					FPuts(OutHandle, "Man : Could not allocate structure for ExAll()\n");

				FreeDosObject(DOS_RDARGS, (void *)ManRDArgs);
			}
			else
				FPuts(OutHandle, "Man : Could not allocate structure for MANOPT parsing\n");

			FreeVec(ManArgs);
		}
		else
			FPuts(OutHandle, "Man : Could not allocate buffer for MANOPT parsing\n");

		FreeVec(Args);
	}
	else
		FPuts(OutHandle, "Could not allocate buffer for CommandLine Parsing\n");

	if(Found && !Breaked)
	{
		SystemTags(SearchName, TAG_DONE);

			// Free buffers

		if(MainBuffer)
			FreeVec(MainBuffer);

		CloseLibrary((struct Library *)DOSBase);
		return(0);
	}
	else if(Args && RDArgs)
	{
		if(StartedSearch)
		{
			if(!Breaked)
			{
				FPuts(OutHandle, "Man-Pages not found for : ");
				FPuts(OutHandle, FileName);
				FPuts(OutHandle, "\n");
			}
			else
				FPuts(OutHandle, "Man : ^C...\n");
		}

			// Free buffers

		if(MainBuffer)
			FreeVec(MainBuffer);

		CloseLibrary((struct Library *)DOSBase);
		return(10);
	}

	if(MainBuffer)
		FreeVec(MainBuffer);

	CloseLibrary((struct Library *)DOSBase);
	return(20);
}





/**********************************************************************/
/*          Get portions from the Env-Var -> Next directory           */
/**********************************************************************/
static char *GetDir(char *NewDir, char *OldDir)
{
	while(*OldDir == ' ')
		OldDir++;

	while((*OldDir != '\n') && (*OldDir != '\0') && (*OldDir != '|') && (*OldDir != ','))
		*NewDir++ = *OldDir++;

	*NewDir		= '\0';

	if(*OldDir == '\n' || *OldDir == '\0')
		OldDir = NULL;
	else
		OldDir++;

	return(OldDir);
}
