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

void PrintUsage(const char *prg,const char *optstr);

/*------------------------------------------------------------------------*/
/*                                                                        */
/*------------------------------------------------------------------------*/
int main(int argc,char **argv,char **envp)
{
   int status=0,arg,i;
   int fdos[2],ferr[2];
   int dosemu_pid;
   char DosCmd[1025];
   
   /* define command line options */
   const char *optstr="e:h";
   
   /* process each command line argument */
   while ((arg=getopt(argc,argv,optstr))!=-1)
   {
      switch (arg)
      {
         /* add an object to the environment */
         case 'e': {putenv(optarg); break;}

         /* check for usage-query */
         case 'h': {PrintUsage(argv[0],optstr); break;}
            
         /* unimplemented feature */
         default: {exit(-1);}
      }
   }

   /* check for missing DOS command */
   if (!argv[optind]) {fprintf(stderr,"%s: Missing DOS command.\n",argv[0]); exit(-1);}

   /* concatenate remaining command line arguments into one string */
   else for (DosCmd[0]=0,i=optind; i<argc; i++)
   {
      /* protect against buffer overflow */
      if ((strlen(DosCmd)+strlen(argv[i]))>sizeof(DosCmd)-2)
      {
         fprintf(stderr,"%s: DOS command too long; limit is 1024 bytes.\n",argv[0]);
         exit(-1);
      }

      /* contatenate current command line arguement to command string */
      else {strcat(DosCmd,argv[i]); strcat(DosCmd," ");}
   }
   
   /* create a pipe for connection to DOSEMU's stdout stream */
   if (pipe(fdos) == -1)
   {
      fprintf(stderr,"%s: Attempt to create pipe(fdos) failed:\n"
                     "    ErrNo[%d]: %s\n",argv[0],errno,strerror(errno));
      exit(-2);
   }
 
   /* create a pipe for connection to DOSEMU's stderr stream */
   else if (pipe(ferr) == -1)
   {
      fprintf(stderr,"%s: Attempt to create pipe(ferr) failed:\n"
                     "    ErrNo[%d]: %s\n",argv[0],errno,strerror(errno));
      exit(-3);
   }

   /* fork and record the child's PID */
   else switch((dosemu_pid=fork()))
   {
      /* check if the fork failed */
      case -1: 
      {
         fprintf(stderr,"%s: fork() failed: ErrNo[%d]: %s\n",
                 argv[0],errno,strerror(errno));

         status=-4; break;
      }

      /* DOSEMU will be launched in the child process */
      case 0:
      {
         /* associate DOSEMU's stdout with write-end of dos-pipe */
         dup2(fdos[1],fileno(stdout)); close(fdos[0]); close(fdos[1]);

         /* associate DOSEMU's stderr with write-end of err-pipe */
         dup2(ferr[1],fileno(stderr)); close(ferr[0]); close(ferr[1]);

         /* execute DOSEMU in dumb mode with the DOS command */
         if (execlp("dosemu","dosemu","-dumb",DosCmd,(const char *)NULL)<0)
         {
            fprintf(stderr,"%s: Attempt to execute dosemu failed: ErrNo[%d]: %s\n",
                    argv[0],errno,strerror(errno));
            status=-5; 
         }

         break;
      }

      /* the parent process will analyze the output of the DOS command */
      default:
      {
         FILE *dos,*err; char buf[1024]; 
         
         /* parent will use read-ends of dos & err pipes, close the write-ends */
         close(fdos[1]); close(ferr[1]);

         /* convert the pipes into buffered streams */
         if (!(dos=fdopen(fdos[0],"r")) || !(err=fdopen(ferr[0],"r")))
         {
            fprintf(stderr,"%s: Attempt to associate stdio streams to pipes failed.\n",argv[0]);
            close(fdos[0]); close(ferr[0]);
            status=-6;
         }
         else
         {
            /* read error strings from DOSEMU and write to stderr */
            while (fgets(buf,sizeof(buf),err))
            {
               int n=strcspn(buf,"\r\n"); buf[n]=0;
               status=1; fprintf(stderr,"%s\n",buf);
            }

            /* read output of DOS command and write to stdout */
            while (fgets(buf,sizeof(buf),dos))
            {
               int n=strcspn(buf,"\r\n"); buf[n]=0;
               if (strstr(buf,"(error)")) status=1;
               fprintf(stdout,"%s\n",buf);
            }

            /* close the pipes */
            fclose(err); fclose(dos);
         }
      }
   }

   return status;
}


/*------------------------------------------------------------------------*/
/* function to print a terse usage instruction                            */
/*------------------------------------------------------------------------*/
void PrintUsage(const char *prg,const char *optstr)
{
   fprintf(stderr,"Usage: %s [%s] \"dos-command\"\n"
           " -e\"string\"    Add a string to the environment.\n"
           " -h            Print these terse usage instructions.\n",prg,optstr);
}
