
/*
 * Nom		  : NModem
 * Auteur         : Thibaut MAQUET
 * Version        : $VER: NModem 1.11  
 * Langage        : Lattice C 6.0
 * Configuration  : AMIGA 1200 6Mo 80HD Kick3.0
 * Date           : Mardi 22-Juin-93  18:11:30
 * Description    : Transmet des fichiers ou des messages entre
 *                  deux AMIGAS reliés par le port série.
 *                  
 *                  Arexx Port : NMODEM
 *
 *                  Description des commandes : 
 *                  
 *                  SendTxt "message".... Envoit un message 
 *                  GetTxt............... Recoit un message
 *                  SendFile "fichier"... Envoit un fichier 
 *                  GetFile "fichier".... Recoit un fichier
 *                  About................ Ecrit "à propos"
 *                  Halt................. Exit
 *            
 *      
 */

#include <clib/alib_protos.h>
#include <proto/exec.h>
#include <proto/dos.h>
#include <proto/intuition.h>
#include <intuition/intuition.h>
#include <graphics/gfxbase.h>
#include <exec/memory.h>
#include <exec/ports.h>
#include <exec/io.h>
#include <devices/serial.h>
#include <proto/reqtools.h>
#include <libraries/reqtools.h>
#include <rexx/myminrexx.h>

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

char *Version = "$VER: NMODEM 1.11 ";

struct MsgPort	    *SerialMP = NULL;
struct MsgPort      *MyPort = NULL;
struct IOExtSer	    *SerialIO = NULL;
struct FileHandle   *FichHandle;
struct IntuiBase    *IntuiBase = NULL;
struct GfxBase      *GfxBase = NULL;
struct DosBase      *DosBase = NULL;
struct Window       *Win;
struct IntuiMessage *msg;
struct ReqToolsBase *ReqToolsBase = NULL;
struct RxsLib 	    *RexxSysBase = NULL;

char txt0[160],txt[160];
char *adr_ptr;
int octets_lus;
BOOL serflag = FALSE;

#define NMODEM "NMODEM"
#define ADR_FileExchange (char *)&FileExchange
#define SIZE_FileExchange sizeof(struct st_FileExchange)

struct st_FileExchange 
{
  char nom[160];
  int taille;  
}FileExchange;

struct IntuiText IText1 = {3,1,COMPLEMENT,0,0,NULL,"Quitte",NULL};
struct MenuItem MenuItem4 = {NULL,0,24,64,8,ITEMTEXT+ITEMENABLED+HIGHCOMP,
0,(APTR)&IText1,NULL,NULL,NULL,MENUNULL};
struct IntuiText IText2 = {3,1,COMPLEMENT,0,0,NULL,"A propos",NULL};
struct MenuItem MenuItem3 = {&MenuItem4,0,16,64,8,ITEMTEXT+ITEMENABLED+HIGHCOMP,
0,(APTR)&IText2,NULL,NULL,NULL,MENUNULL};
struct IntuiText IText3 = {3,1,COMPLEMENT,0,0,NULL,"Recevoir",NULL};
struct MenuItem SubItem2 = {NULL,49,7,64,8,ITEMTEXT+ITEMENABLED+HIGHCOMP,
0,(APTR)&IText3,NULL,NULL,NULL,MENUNULL};
struct IntuiText IText4 = {3,1,COMPLEMENT,0,0,NULL,"Envoyer",NULL};
struct MenuItem SubItem1 = {&SubItem2,49,-1,64,8,ITEMTEXT+ITEMENABLED+HIGHCOMP,	
0,(APTR)&IText4,NULL,NULL,NULL,MENUNULL};
struct IntuiText IText5 = {3,1,COMPLEMENT,0,0,NULL,"Fichier",NULL};
struct MenuItem MenuItem2 = {&MenuItem3,0,8,64,8,ITEMTEXT+ITEMENABLED+HIGHCOMP,	
0,(APTR)&IText5,NULL,NULL,&SubItem1,MENUNULL};
struct IntuiText IText6 = {3,1,COMPLEMENT,0,0,NULL,"Recevoir",NULL};
struct MenuItem SubItem4 = {NULL,49,7,64,8,ITEMTEXT+ITEMENABLED+HIGHCOMP,
0,(APTR)&IText6,NULL,NULL,NULL,MENUNULL};
struct IntuiText IText7 = {3,1,COMPLEMENT,0,0,NULL,"Envoyer",NULL};
struct MenuItem SubItem3 = {&SubItem4,49,-1,64,8,ITEMTEXT+ITEMENABLED+HIGHCOMP,	
0,(APTR)&IText7,NULL,NULL,NULL,MENUNULL};
struct IntuiText IText8 = {3,1,COMPLEMENT,0,0,NULL,"Texte",NULL};
struct MenuItem MenuItem1 = {&MenuItem2,0,0,64,8,ITEMTEXT+ITEMENABLED+HIGHCOMP,	
0,(APTR)&IText8,NULL,NULL,&SubItem3,MENUNULL};
struct Menu Menu1 = {NULL,0,0,63,0,MENUENABLED,"Options",&MenuItem1};
struct NewWindow NewWindowStructure1 = {0,0,250,11,0,1,CLOSEWINDOW+MENUPICK,
WINDOWSIZING+WINDOWDRAG+WINDOWDEPTH+WINDOWCLOSE+ACTIVATE+NOCAREREFRESH,NULL,
NULL,"NModem 1.1",NULL,NULL,11,11,(UWORD)-1,(UWORD)-1,WBENCHSCREEN};


/*
 * Ferme tout et quitte
 */

void QuittePgm(char *err)
{
  if (DosBase)
    CloseLibrary( (struct Library *)DosBase );
  if (IntuiBase)
    CloseLibrary( (struct Library *)IntuiBase );
  if (GfxBase)
    CloseLibrary( (struct Library *)GfxBase );
  if (ReqToolsBase)
    CloseLibrary( (struct Library *)ReqToolsBase);
 if (RexxSysBase)
    CloseLibrary( (struct Library *)RexxSysBase );
  if (!serflag)
    CloseDevice( (struct IORequest *)SerialIO ); 
  if (SerialIO)
    DeleteExtIO( (struct IORequest *)SerialIO );  
  if (SerialMP)
    DeletePort(SerialMP);
  if (MyPort)
    DeletePort(MyPort);
  if (err)
    puts(err);
  Exit(0);
}


/*
 * Ajouter un port
 */

struct MsgPort *Add_A_Port(char *NomPort)
{
  struct MsgPort *Port;

  Forbid();
  if (FindPort(NomPort) )
  {
    Permit();
    return NULL;
  }
 
  Port = CreatePort(NomPort,0L);
  Permit();
  return(Port);
}


/*
 * Allouer un buffer en fonction de la configuration
 */

char *Alloue(int taille)
{
  char *ptr = NULL;  
  ptr = AllocMem(taille,MEMF_FAST | MEMF_CLEAR );
  if (!ptr)   
    ptr = AllocMem(taille,MEMF_CHIP | MEMF_CLEAR );
  return ptr;
}


/*
 * Cherche le chemin d'accès d'un fichier via la reqtools.library
 */

char *rt_getpathname(char *intitule)
{

  struct rtFileRequester *filereq;
  char nom_fichier[80],chemin_acces[80];
  
  nom_fichier[0] = '\0';
  if (filereq = rtAllocRequestA (RT_FILEREQ, NULL)) 
  {
    if (rtFileRequest (filereq,nom_fichier,intitule,TAG_END))
    {
      strcpy(chemin_acces,filereq->Dir);      
      if (chemin_acces[strlen(chemin_acces) - 1] != ':')
        strcat(chemin_acces,"/");
      strcat(chemin_acces,nom_fichier);
      return ( (char *)chemin_acces );
    }
    else
      return 0;  
    rtFreeRequest (filereq);
  }
  else
    return 0;
}


/*
 * Envoyer le buffer sur RS232
 */

ULONG WriteRS232(char *buff,int len)
{
  SerialIO->IOSer.io_Length = len;
  SerialIO->IOSer.io_Data = (APTR)buff;
  SerialIO->IOSer.io_Command = CMD_WRITE;
  if (!DoIO( (struct IORequest *)SerialIO) )
    return SerialIO->IOSer.io_Actual;
  else
    return 0;
}


/*
 * Lire le buffer sur RS232
 */

ULONG ReadRS232(char *buff,int len)
{
    SerialIO->IOSer.io_Length = len;
    SerialIO->IOSer.io_Data = (APTR)buff;
    SerialIO->IOSer.io_Command = CMD_READ;
    if (!DoIO( (struct IORequest *)SerialIO) )
      return SerialIO->IOSer.io_Actual;
    else
      return 0;
}


/*
 * Envoyer le buffer sur RS232 renvoit 0 si erreur sinon octets écrits
 */

ULONG WRRS232(char *buff,int len)
{
  char out[15];

  stci_d(out,len);
  if ( WriteRS232(out,15) )
    return ( WriteRS232(buff,len) );    
  else
    return 0;
}


/*
 * Lire le buffer sur RS232 renvoit 0 si erreur sinon octets lus
 */

ULONG RWRS232(char *buff,int len)
{
  char tmp[15];

  if ( ReadRS232(tmp,15) )
  {
    stcd_i(tmp,&len);
    return (ReadRS232(buff,len)); 
  }
  else
    return 0;
}


/*
 * Récuperer du texte sur la RS232
 */

int ReportText(void)
{
  if ( !ReadRS232(txt0,160) )
  {
    printf("IO a échoué\n");
    return 0;
  }
  if (txt0[0] == '!')
    return 0;
  else
  {
    printf("%s\n",txt0);
    return -1;
  }
}


/*
 * Envoyer le texte sur RS232
 */

void SendText(char *TextSent)
{
  if ( !WriteRS232(TextSent,160) )
    printf("IO a échoué\n");          
}


/*
 * Attente et affichage du texte
 */

void WaitText(void)
{
  printf("\nNModem dialogue interactif en marche.\n");
  if (ReportText())
    while (ReportText())
      continue;
  printf("\nNModem dialogue interactif désactivé.\n");
}


/*
 * Saisie et envoi du texte
 */

void GetText(void)
{
  printf("\nNModem dialogue interactif en marche, tapez '!' pour arrêter.\n");
  printf(": ");
  gets(txt);
  if (txt[0] == '!')
    SendText(txt);
  while (txt[0] != '!')
  {   
    SendText(txt);
    printf(": ");
    gets(txt);
    if (txt[0] == '!')
      SendText(txt);
  }
  printf("\nNModem dialogue interactif désactivé.\n");
}

/*
 * Envoit un fichier
 */

void SendFile(char *fichier)
{
  adr_ptr = Alloue(50*1024);
  if (!adr_ptr)  
    printf("Pas de mémoire\n"); 
  else
  {
    strcpy(FileExchange.nom,fichier);

    FileExchange.taille = 160;
    WriteRS232(ADR_FileExchange,SIZE_FileExchange);

    FichHandle = (struct FileHandle *)Open(fichier,MODE_OLDFILE);
    if (!FichHandle)
      printf("Impossible d'ouvrir le fichier '%s'\n",FileExchange.nom);
    else
    {
      octets_lus = Read((BPTR)FichHandle,adr_ptr,50*1024);
      while (octets_lus)
      {
        WRRS232(adr_ptr,octets_lus);
        octets_lus = Read((BPTR)FichHandle,adr_ptr,50*1024);
      }
      Close((BPTR)FichHandle);
      printf("\nFichier '%s' expédié.\n",FileExchange.nom);
    } 
    FreeMem(adr_ptr,50*1024);     
  } 
}


/*
 * Recevoir un fichier
 */

void ReportFile(char *FichierNom)
{
  printf("\nFichier en attente ...\n");

  ReadRS232(ADR_FileExchange,SIZE_FileExchange);
  if ( (adr_ptr = Alloue(50*1024)) )
  {

    FichHandle = (struct FileHandle *)Open(FichierNom,MODE_NEWFILE);
 
    if (!FichHandle)
      printf("Impossible d'ouvrir le fichier\n");
    else
    {
      octets_lus = RWRS232(adr_ptr,50*1024);
      while (octets_lus)
      {
        Write((BPTR)FichHandle,adr_ptr,octets_lus);
        if (octets_lus == 50*1024)
          octets_lus = RWRS232(adr_ptr,50*1024);
        else
          octets_lus = 0;
      }
      Close((BPTR)FichHandle);
      printf("\nFichier '%s' recu.\n",FichierNom);
    }
    FreeMem(adr_ptr,50*1024);
  }
  else
    printf("Pas de mémoire\n");
}


/*
 * Entête
 */
        
void EcritEntete(void)
{
  printf("NModem version 1.11  © Thibaut MAQUET - 1993\n");
}


/*
 * Ecrit About
 */

void WriteAbout(void)
{ 
  rtEZRequest ("NModem version 1.1  © Thibaut MAQUET - 1993\nArexx port : 'NMODEM'","Ok", NULL, NULL);
}


/*
 * Ouvre tout le bazar
 */

void OuvreTout(void)
{
  if ( !(DosBase = (struct DosBase *)OpenLibrary("dos.library",0L)) )
    QuittePgm("Pas de dos.library");
  if ( !(IntuiBase = (struct IntuiBase *)OpenLibrary("intuition.library",0L)) )
    QuittePgm("Pas de intuition.library");
  if ( !(GfxBase   = (struct GfxBase *)OpenLibrary("graphics.library",0L)) )
    QuittePgm("Pas de graphics.library");
  if ( !(ReqToolsBase = (struct ReqToolsBase *)OpenLibrary (REQTOOLSNAME, REQTOOLSVERSION)) )
    QuittePgm("reqtools.library V37 introuvable");
  if ( !(SerialMP = Add_A_Port(0)) )
    QuittePgm("Erreur, le port ne peut être crée");
  if ( !(SerialIO = (struct IOExtSer *) CreateExtIO(SerialMP,sizeof(struct IOExtSer))) )
    QuittePgm("Erreur, la requete IO ne peut être crée");
  SerialIO->io_SerFlags = SERF_SHARED;
  if (OpenDevice(SERIALNAME,0,(struct IORequest *)SerialIO,0L) )
  {
    serflag = TRUE;
    QuittePgm("Erreur, le pilote RS232 (port série) n'existe pas");
  }
}



/*
 * Renvoie le message envoyé par ARexx avec les résultats adéquats.
 */

void ReplyRexxCommand(struct RexxMsg *RxMsg,long primary,long secondary,char *result)
{
  if(!primary && (RxMsg->rm_Action & RXFF_RESULT))  
    secondary = (result ? (LONG)CreateArgstring(result,strlen(result)) : 0L);
  RxMsg->rm_Result1 = primary;
  RxMsg->rm_Result2 = secondary;
  ReplyMsg((struct Message *)RxMsg);
}


/*
 * Fonction chargée d'interpréter les commandes AREXX
 */

BOOL InterpretCommand(struct RexxMsg *msg)
{
  char *result="1";
  long primary = 0,secondary = 0; 
  BOOL termine = TRUE;
  char command[80],pathname[160];
  int i;

  for (i=0;i<80;i++)
    command[i] = '\00';

  for (i=0;i<strlen(msg->rm_Args[0]);i++)
    command[i] = toupper(msg->rm_Args[0][i]); 

  if (!strcmp(command,"HALT"))
    termine = FALSE;
  else
  {
    if (!memcmp(command,"SENDTXT",strlen("SENDTXT")) )
      SendText(&command[8]);
    else
    {
      if (!memcmp(command,"GETTXT",strlen("GETTXT")) )
        ReportText();
      else
      {
        if (!memcmp(command,"SENDFILE",strlen("SENDFILE")) )
        {
          pathname[0] = '\0';
          if (command[9])
            strcpy(pathname,&command[9]);
          else
            strcpy(pathname,rt_getpathname("Fichier à envoyer"));
          if (pathname[0])
            SendFile(pathname);
        }
        else
        {
          if (!memcmp(command,"GETFILE",strlen("GETFILE")) )
          {
            pathname[0] = '\0';
            if (command[8])
              strcpy(pathname,&command[8]);
            else
              strcpy(pathname,rt_getpathname("Fichier à recevoir"));
            if (pathname[0])
              ReportFile(pathname);
          }
          else
            if (!memcmp(command,"ABOUT",strlen("ABOUT")) )
              WriteAbout();
            else
              ERR_BAD_TOKEN;
        }
      }
    }
  }

  ReplyRexxCommand(msg,primary,secondary,result);
  return (termine);
}


/*
 * Attendre un evenement AREXX
 */

void WaitArexx(void)
{
  BOOL termine = TRUE;
  struct RexxMsg *RxMsg;

  if (!(RexxSysBase = (struct RxsLib *)
     OpenLibrary("rexxsyslib.library",0L)) )
    QuittePgm("Pas de rexxsyslib.library");
  if(!(MyPort=Add_A_Port(NMODEM)))
    QuittePgm("Impossible de créer le port AREXX");

  while(termine)
  {
    WaitPort(MyPort);
    while(termine && (RxMsg = (struct RexxMsg *)GetMsg(MyPort)))
      termine = InterpretCommand(RxMsg);
  }
}



/*
 * fonction principale
 */

main(int argc,char **argv)
{
  USHORT code;
  ULONG class, menu,item,sub;
  APTR address;
  BOOL quit = FALSE;

  OuvreTout();

/*
 * Utilisation au CLI
 */

  if (argc > 1)
  {
    EcritEntete();

    if (!strcmp(argv[1],"-t"))
      GetText();
    else
      if (!strcmp(argv[1],"-r"))
        WaitText();
      else
        if (!strcmp(argv[1],"-a"))
        {
          if (argv[2])
            SendFile(argv[2]);
          else
          {
            strcpy(txt0,rt_getpathname("Fichier à envoyer"));
            if (txt0[0])  
              SendFile(txt0);
            else
              QuittePgm(0);
          }
        }
        else
          if (!strcmp(argv[1],"-b"))
          {
            if (argv[2])
              ReportFile(argv[2]);
            else
            {
              strcpy(txt0,rt_getpathname("Fichier à recevoir"));
              if (txt0[0])  
                ReportFile(txt0);
              else
                QuittePgm(0);
            }
          }
          else
            if ( !strcmp(strupr(argv[1]),"AREXX") )
            {
              printf("\nNModem en attente d'évenements AREXX ...\n");
              WaitArexx();
            }
            else
              if (!strcmp(argv[1],"?"))
              {
                printf("\nUsage : NModem -t pour envoyer des messages\n        NModem -r pour recevoir des messages\n        NModem -a pour envoyer un fichier\n        NModem -b pour recevoir un fichier\n        NModem arexx pour recevoir des requêtes AREXX (Port : NMODEM)\n\nCommandes AREXX :\nSendTxt - GetTxt - SendFile - GetFile - About - Halt\n\n");
              }        
  }

/* 
 * Utilisation de la fenêtre
 */

  else
  {

    Win = (struct Window *)OpenWindow(&NewWindowStructure1);
    SetMenuStrip(Win,&Menu1);

    while(!quit)
    {
      WaitPort(Win->UserPort);
      while(msg = (struct IntuiMessage *)GetMsg(Win->UserPort))
      {
        code = msg->Code;
        address = msg->IAddress;
        class = msg->Class;
        ReplyMsg((struct Message *) msg);
    
        switch(class)
        {
          case CLOSEWINDOW:
            quit=TRUE;
            break; 
          case MENUPICK:
            while(code != MENUNULL)
            {
              menu = MENUNUM(code);
              item = ITEMNUM(code);
              sub = SUBNUM(code);
              switch(menu)
              {
                case 0:
                  switch(item)
                  {
                    case 0 :
                      switch(sub)
                      {
                        case 0:
                          GetText();
                          break;
                        case 1:
                          WaitText();
                          break;
                      }
                      break;
                    case 1 :
                      switch(sub)
                      {
                        case 0:

			  /*
  			   * Bug Lattice ??    =:^( )^8=
                           * SendFile(rt_getpathname("Fichier à envoyer")); ??
                           */

                          strcpy(txt0,rt_getpathname("Fichier à envoyer"));
                          if (txt0[0])  
                            SendFile(txt0);
                          break;
                        case 1:
                          strcpy(txt0,rt_getpathname("Fichier à recevoir"));
                          if (txt0[0])  
                            ReportFile(txt0);
                          break;
                      }
                      break;
                    case 2 :
                      WriteAbout();   
                      break;
                    case 3 :
                      quit=TRUE;
                      break;
                    default :
                      break;
                  }
                  break;
             
                default:
                  break;
              }
              code = ((struct MenuItem *)ItemAddress(&Menu1,(LONG)code))->NextSelect;
            }
        }
      }
    } 
    CloseWindow(Win);
  }   
  QuittePgm(0);
}
