mcloud/
/***************************************************************************
 *  Original Diku Mud copyright (C) 1990, 1991 by Sebastian Hammer,        *
 *  Michael Seifert, Hans Henrik St{rfeldt, Tom Madsen, and Katja Nyboe.   *
 *                                                                         *
 *  Merc Diku Mud improvments copyright (C) 1992, 1993 by Michael          *
 *  Chastain, Michael Quan, and Mitchell Tse.                              *
 *                                                                         *
 *  In order to use any part of this Merc Diku Mud, you must comply with   *
 *  both the original Diku license in 'license.doc' as well the Merc       *
 *  license in 'license.txt'.  In particular, you may not remove either of *
 *  these copyright notices.                                               *
 *                                                                         *
 *  Dystopia Mud improvements copyright (C) 2000, 2001 by Brian Graversen  *
 *                                                                         *
 *  Much time and thought has gone into this software and you are          *
 *  benefitting.  We hope that you share your changes too.  What goes      *
 *  around, comes around.                                                  *
 ***************************************************************************/

#if defined(macintosh)
#include <types.h>
#else
#include <sys/types.h>
#endif
#include <ctype.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <time.h>
#include "merc.h"

PROJECT_DATA 	* get_project_by_number args( ( int pnum ) );
NOTE_DATA 	* get_log_by_number  	args( ( PROJECT_DATA *pproject, int pnum ) );

PROJECT_DATA *		first_project;
PROJECT_DATA *		last_project;
PROJECT_DATA * read_project args( ( FILE *fp ) );

void   note_attach         args( ( CHAR_DATA *ch ) );
void   write_projects      args( ( void ) );
void   delete_project      args( ( PROJECT_DATA *proj ) );

void load_projects( void )
{
    FILE *fp;
    PROJECT_DATA *proj;

    first_project = NULL;
    last_project = NULL;

    if( ( fp = fopen(PROJECTS_FILE, "r" ) ) == NULL )
        return;

    while( (proj = read_project( fp ) ) )
    {
      LINK( proj, first_project, last_project, next, prev );
    }

    return;
}

NOTE_DATA * read_log( FILE *fp )
{
    NOTE_DATA * log;
    char * word;

    log = alloc_mem( sizeof( NOTE_DATA ) );
    
    for ( ; ; )
    {
        word = fread_word( fp );
        if ( !str_cmp( word, "Sender" ) )
            log->sender   = fread_string( fp );
        else if ( !str_cmp( word, "Date" ) )
            log->date     = fread_string( fp );
        else if ( !str_cmp( word, "Subject" ) )
            log->subject  = fread_string( fp );
        else if ( !str_cmp( word, "Text" ) )
            log->text     = fread_string( fp );
        else if ( !str_cmp( word, "Endlog" ) )
        {
            fread_to_eol( fp );
            log->next             = NULL;
            return log;
        }
        else
        {
            free_mem( log, sizeof( NOTE_DATA ) );
            bug( "read_log: bad key word.", 0 );
            return NULL;
        }
    }
}

#if defined(KEY)
#undef KEY
#endif

#define KEY( literal, field, value )    if ( !str_cmp( word, literal ) ){field  = value;fMatch = TRUE;break;}
PROJECT_DATA * read_project( FILE *fp )
{
    PROJECT_DATA *project;
    NOTE_DATA *log, *tlog;
    char *word;
    char buf [MSL];
    bool fMatch;
    char letter;

    do
    {
        letter = getc( fp );
        if( feof(fp) )
        {
            fclose( fp );
            return NULL;
        }
    }

    while( isspace(letter) )
        ;
    ungetc( letter, fp );

    project = alloc_mem( sizeof( PROJECT_DATA ) );

    project->first_log = NULL;
    project->next       = NULL;
    project->coder = NULL;
    project->description = str_dup("");
    project->name = str_dup( "" );
    project->owner = str_dup( "" );
    project->date = str_dup( "Not Set?!");
    project->status = str_dup( "No update." );

    for ( ; ; )
    {
        word   = feof( fp ) ? "End" : fread_word( fp );
        fMatch = FALSE;

        switch ( UPPER(word[0]) )
        {
        case '*':
            fMatch = TRUE;
            fread_to_eol( fp );
            break;
	case 'C':
		KEY("Coder",	project->coder,fread_string( fp )  );
		break;
	case 'D':
		if (!str_cmp(word, "Date") )
			free_string(project->date);
		else if ( !str_cmp(word, "Description") )
			free_string(project->description);
		KEY("Date",		project->date,fread_string( fp ));		
	   	KEY("Description",project->description,fread_string(fp));
		break;
        case 'E':
            if ( !str_cmp( word, "End" ) )
            {   
                if ( !project->description )
                    project->description = str_dup( "" );
                if ( !project->name )
                    project->name = str_dup( "" );
                if ( !project->owner )
                    project->owner = str_dup( "" );
                if ( !project->date )
	        project->date = str_dup( "Not Set?!" );
                if ( !project->status )
                    project->status = str_dup( "No update." );
                if ( str_cmp(project->owner, "None") )
                    project->taken = TRUE;
                return project;
            }
            break;
	case 'L':
	    if( !str_cmp( word, "Log" ) )
	    {
		fread_to_eol( fp );
		log = read_log( fp );
		if( !log )
		{
		   sprintf( buf, "read_project: couldn't read log,aborting");
		   bug( buf, 0 );
		   exit( 1 );
	        }
		if(!log->sender)
		  log->sender = str_dup( "" );
		if(!log->date)
		  log->date   = str_dup( "" );
		if(!log->subject)
		  log->subject = str_dup( "None" );
		  log->to_list = str_dup( "" );
                  if( !project->first_log )
                      project->first_log = log;
                  else
                  {
                      log->next = project->first_log;
                      project->first_log = log;
                  }

		fMatch = TRUE;
		break;
	    }
		break;
	case 'N':
	    KEY( "Name", 		project->name,fread_string( fp )  );
		break;
	case 'O':
	    KEY( "Owner", 		project->owner,fread_string( fp ) );
		break;
	case 'S':
	    KEY( "Status", 		project->status,fread_string( fp ) );
		break;
        }
        if ( !fMatch ) 
        {
            sprintf( buf, "read_project: no match: %s", word );
            bug( buf, 0 );
        }
    }


    for( log = project->first_log; log; log = tlog )
    {
          tlog = log->next;
          free_string( log->text    );
          free_string( log->subject );
          free_string( log->to_list );
          free_string( log->date    );
          free_string( log->sender  );
          log->next = note_free;
          note_free   = log;
    }
    project->first_log =NULL;
    if ( project->coder )
        free_string( project->coder );
    if ( project->description )
        free_string( project->description );
    if ( project->name )
        free_string( project->name );
    if ( project->owner )
        free_string( project->owner );
    if ( project->date )
        free_string( project->date );
    if ( project->status )
        free_string( project->status );

    free_mem( project, sizeof( PROJECT_DATA ) );
    return project;
}

void do_project( CHAR_DATA *ch, char *argument )
{
   char arg[MAX_INPUT_LENGTH];
   char arg1[MAX_INPUT_LENGTH];
   char arg2[MAX_INPUT_LENGTH];
   char buf[MSL];
   int pcount;
   int pnum;
   PROJECT_DATA *pproject;

   if( IS_NPC( ch ) )
      return;

   if( !ch->desc )
   {
      bug( "do_project: no descriptor", 0 );
      return;
   }

   argument = one_argument( argument, arg );
   smash_tilde( argument );

   if( !str_cmp( arg, "save" ) )
   {
      write_projects();
      send_to_char ("Projects saved.\n\r",ch);
      return;
   }

   if ( !str_cmp( arg, "code" ) )
   {
      pcount = 0;
	send_to_char( 
" ## | Owner       | Project              |\n\r",ch);
	send_to_char(
"---|-------------|----------------------|--------------------------|-----------\n\r",ch);
      for( pproject = first_project; pproject; pproject = pproject->next )
      {
	 pcount++;
 	if ( (pproject->status && str_cmp(pproject->status, "approved"))
		 || pproject->coder != NULL)  
		continue;
	sprintf(buf, "%2d | %-11s | %-20s |\n\r", 
	    pcount, 
	    pproject->owner ? pproject->owner : "(None)", 
 	    pproject->name);
        send_to_char(buf, ch);
      }
	return;
   }
   if ( !str_cmp( arg, "more" ) || !str_cmp( arg, "mine" ) )
   {
        NOTE_DATA *log;
	bool MINE = FALSE;
	int num_logs=0;
	pcount = 0;
	
	if ( !str_cmp( arg, "mine" ) )
		MINE = TRUE;

	send_to_char("\n\r",ch);
	send_to_char( 
" ## | Owner       | Project              | Coder         | Status     | # of Logs\n\r",ch);
	send_to_char(
"---|-------------|----------------------|---------------|------------|----------\n\r", ch);
      for( pproject = first_project; pproject; pproject = pproject->next )
      {
	   pcount++;
	   if ( MINE &&(!pproject->owner||str_cmp( ch->name,pproject->owner)) 
		&& (!pproject->coder || str_cmp( ch->name, pproject->coder)) )
		continue;
	   else if (!MINE && pproject->status && 
			!str_cmp("Done", pproject->status ) )
		continue;
	   num_logs = 0;
	   for ( log = pproject->first_log; log; log=log->next )
		num_logs++;
	   sprintf(buf, "%2d | %-11s | %-20s | %-13s | %-10s | %3d\n\r", 
	    pcount, 
	    pproject->owner ? pproject->owner : "(None)", 
 	    pproject->name,
	    pproject->coder ? pproject->coder : "(None)",
	    pproject->status ? pproject->status : "(None)",
	     num_logs);
	   send_to_char(buf, ch);
       }
	return;
   }
   if( arg[0] == '\0' || !str_cmp( arg, "list" ) )
   {
      bool aflag, projects_available;
      aflag = FALSE;
      projects_available = FALSE;
      if( !str_cmp( argument, "available" ) )
      	aflag = TRUE;

      send_to_char("\n\r",ch);
      if( !aflag )
      {
	send_to_char( 
" ## | Owner       | Project              | Date                     | Status\n\r",ch);
	send_to_char(
"---|-------------|----------------------|--------------------------|-----------\n\r",ch);
      }
      else
      {
	send_to_char(" ## | Project              | Date\n\r",ch);
	send_to_char(
"---|----------------------|--------------------------\n\r",ch);
      }
      pcount = 0;
      for( pproject = first_project; pproject; pproject = pproject->next )
      {
	 pcount++;
	 if (pproject->status && !str_cmp("Done", pproject->status ) )
		continue;
 	 if(!aflag)
	   sprintf(buf, "%2d | %-11s | %-20s | %-24s | %-10s\n\r", 
	    pcount, 
	    pproject->owner ? pproject->owner : "(None)", 
 	    pproject->name,
	    pproject->date,
	    pproject->status ? pproject->status : "(None)");
	 else
	   if( !pproject->taken )
	   {
	      if( !projects_available )
		projects_available = TRUE;
	      sprintf(buf, "%2d | %-20s | %s\n\r",
	      pcount,
	      pproject->name,
	      pproject->date);
	   }
	   send_to_char(buf, ch);
      }
      if(pcount == 0)
         send_to_char("No projects exist.\n\r",ch);
      else if( aflag && !projects_available )
	 send_to_char("No projects available.\n\r",ch); 
      return;
   }

   if( !str_cmp( arg, "add" ) )
   {
      char *strtime;
      PROJECT_DATA *new_project; /* Just to be safe */

      if( get_trust(ch) < LEVEL_JUDGE)
      {
	send_to_char("You are not powerfull enough to add a new project.\n\r",ch);
	return;
      }

      new_project = alloc_mem( sizeof( PROJECT_DATA));	
      LINK( new_project, first_project, last_project, next, prev );
      new_project->name = str_dup(argument);
      new_project->coder = NULL;
      new_project->taken = FALSE;
      new_project->owner = NULL;
      new_project->status = NULL;
      new_project->description = str_dup( "" );
      new_project->first_log = NULL;
      new_project->last_log = NULL;
      strtime                         = ctime( &current_time );
      strtime[strlen(strtime)-1]      = '\0'; 
      new_project->date = str_dup( strtime );
      write_projects();
      send_to_char("Ok.\n\r",ch );
      return;
   }

   if( !is_number( arg ) )   
   {
      send_to_char("Invalid project.\n\r",ch);
      return;
   }
      
   pnum = atoi( arg );
   pproject = get_project_by_number( pnum );
   if(!pproject)
   {
      send_to_char("No such project.\n\r",ch);
      return;
   }

   argument = one_argument( argument, arg1 );

   if( !str_cmp( arg1, "desc" ) )
   {
      if ( pproject->description == NULL )
	pproject->description = str_dup("");
      string_append( ch, &pproject->description );
      return;
   }
   if( !str_cmp( arg1, "delete" ) )
   {
      NOTE_DATA *log, *tlog;
      if( get_trust(ch) < LEVEL_HIGHJUDGE)
      {
	send_to_char("You are not high enough level to delete a project.\n\r",
		ch );
        return;
      }

      tlog = NULL;
      for( log = pproject->first_log; log; tlog = log, log = log->next )
      {
          if ( tlog == NULL )
               pproject->first_log   = log->next;
          else
               tlog->next = log->next;
          free_note( log );
      }
      pproject->last_log = NULL;
      UNLINK( pproject, first_project, last_project, next, prev );

      free_string( pproject->name );
      if ( pproject->coder )
	free_string( pproject->coder );
      if( pproject->owner )
	free_string( pproject->owner );
      if( pproject->description )
        free_string( pproject->description );
      if ( pproject->date )
	free_string( pproject->date );
      if ( pproject->status )
	free_string( pproject->status );

      free_mem( pproject, sizeof( *pproject) );
      write_projects();
      send_to_char("Ok.\n\r",ch );
      return;
   }

   if( !str_cmp( arg1, "take" ) )
   {
      if( pproject->taken  && pproject->owner &&
		!str_cmp(pproject->owner, ch->name ) )
      {
	pproject->taken = FALSE;
	free_string( pproject->owner );
	pproject->owner = NULL;
	send_to_char("You removed yourself as the owner.\n\r", ch );
        write_projects();
	return;
      }
      else if ( pproject->taken )
      {
      	send_to_char("This project is already taken.\n\r" ,ch);
	return;
      }


      if( pproject->owner )
	free_string( pproject->owner );
      pproject->owner = str_dup( ch->name );
      pproject->taken = TRUE;
      write_projects();
      send_to_char("Ok.\n\r" ,ch);
      return;
   }
   if( !str_cmp( arg1, "coder" ) )
   {
	if ( pproject->coder && !str_cmp(ch->name, pproject->coder))
	{
	  free_string( pproject->coder );
	  pproject->coder = NULL;
	  send_to_char("You removed yourself as the coder.\n\r", ch );
      	  write_projects();
	  return;
	}
	else if ( pproject->coder )
	{
	  send_to_char("This project already has a coder.\n\r",ch );
	  return;
	}
       	pproject->coder = str_dup( ch->name );
	write_projects();
	send_to_char("Ok.\n\r" ,ch);
	return;
   }
   if( !str_cmp( arg1, "status" ) )
   {
      if( pproject->owner && str_cmp( pproject->owner, ch->name ) 
	&& get_trust(ch) < LEVEL_JUDGE
	&& pproject->coder 
	&& str_cmp(pproject->coder, ch->name) )
      {  
	send_to_char("This is not your project!\n\r",ch );
	return;
      }
      if(pproject->status)
	free_string(pproject->status);
      pproject->status = str_dup(argument);
      write_projects();
      send_to_char("Done.\n\r", ch );
      return;
   } 
   if( !str_cmp( arg1, "show" ) )
   {
	if ( pproject->description )
	  send_to_char ( pproject->description, ch );
	else
	  send_to_char ("That project does not have a description.\n\r",ch);
	return;
   }
   if( !str_cmp( arg1, "log" ) )
   {
      NOTE_DATA *plog,*tlog;
      if( !str_cmp( argument, "write" ) )
      {
	 string_append( ch, &ch->pnote->text);
	 return;
      }

      argument = one_argument( argument, arg2 );

      if( !str_cmp( arg2, "subject" ) )
      {
	note_attach(ch);
	free_string(ch->pnote->subject);
	ch->pnote->subject = str_dup( argument );
	send_to_char("Ok.\n\r",ch);
	return;
      }

      if( !str_cmp( arg2, "post" ) )
      {
        char *strtime;

        if( pproject->owner && str_cmp(ch->name, pproject->owner) &&
	    pproject->coder && str_cmp(ch->name, pproject->coder) && 
	    get_trust(ch) < LEVEL_JUDGE )
	{
	   send_to_char("This is not your project!\n\r",ch );
	   return;
	}

        if ( !ch->pnote )
        {   
            send_to_char("You have no log in progress.\n\r" ,ch);
            return;
        }

        if ( !ch->pnote->subject )
	{
	    send_to_char( "Your log has no subject.\n\r",ch );
	    return;
	}
        
        strtime                         = ctime( &current_time );
        strtime[strlen(strtime)-1]      = '\0'; 
        ch->pnote->date                 = str_dup( strtime );
	ch->pnote->sender		= ch->name;
        
        plog           = ch->pnote;
        ch->pnote       = NULL;
	if( !pproject->first_log )       
	    pproject->first_log = plog;
	else
	{
	    for( tlog = pproject->first_log; tlog->next; tlog = tlog->next )
	    	;
	    tlog->next = plog;
	}

	plog->next = NULL;
	pproject->last_log = plog;
	write_projects();
        send_to_char("Ok.\n\r",ch);
	return;
      }
      
      if( !str_cmp( arg2, "list" ) )
      {
        if( pproject->owner && pproject->coder 
	    && str_cmp(ch->name, pproject->owner) 
	    && get_trust(ch) < LEVEL_BUILDER
	    && str_cmp(ch->name, pproject->coder) )
	{
	   send_to_char("This is not your project!\n\r",ch );
	   return;
	}

	pcount = 0;
        sprintf(buf, "Project: %-12s: %s\n\r",
         pproject->owner ? pproject->owner : "(None)",
         pproject->name);   
         send_to_char(buf,ch);   	

       	for( plog = pproject->first_log; plog; plog = plog->next )
      	{
           pcount++;
           sprintf( buf, "%2d) %-12s: %s\n\r",
                        pcount,
                        plog->sender,
                        plog->subject );
           send_to_char(buf,ch);
   	}
	if( pcount == 0 )
	   send_to_char("No logs available.\n\r",ch );
	return;
      }
      
      if( !is_number( arg2 ))
      {
	send_to_char("Invalid log.\n\r",ch );
        return;
      }
      
      pnum = atoi( arg2 );

      plog = get_log_by_number( pproject, pnum );	
      if( !plog )
      {
         send_to_char("Invalid log.\n\r",ch );
         return;
      }
	

      if( !str_cmp( argument, "delete" ) )
      {
        if( pproject->owner 
	 && str_cmp(ch->name, pproject->owner) 
	 && get_trust(ch) < LEVEL_JUDGE
         && pproject->coder && str_cmp(ch->name, pproject->coder ) )
	{
	   send_to_char("This is not your project!\n\r",ch );
	   return;
	}
	for( tlog = pproject->first_log; tlog->next; tlog = tlog->next )
		if( tlog->next == plog )
			break;
	tlog->next = plog->next;	
	free_note( plog );
	write_projects();
        send_to_char("Ok.\n\r",ch );
	return;
      }

      if( !str_cmp( argument, "read" ) )
      {
        if( pproject->owner && pproject->coder 
	    && str_cmp(ch->name, pproject->owner) 
	    && get_trust(ch) < LEVEL_BUILDER
	    && str_cmp(ch->name, pproject->coder ) )
	{
	   send_to_char("This is not your project!\n\r" ,ch);
	   return;
	}

	sprintf( buf, "[%3d] %s: %s\n\r%s\n\r%s",
        pnum,
        plog->sender,
        plog->subject,
        plog->date,
        plog->text );
        send_to_char(buf,ch);
	return;
      }
    }
   send_to_char("Unknown syntax see help 'PROJECT'.\n\r", ch );
   return;
}

PROJECT_DATA *get_project_by_number(int pnum)
{
   int pcount;
   PROJECT_DATA *pproject;
   pcount = 0;
   for( pproject = first_project; pproject; pproject = pproject->next )
   {
      pcount++;
      if(pcount == pnum)
         return pproject;
   }
   return NULL;
}

NOTE_DATA *get_log_by_number(PROJECT_DATA *pproject, int pnum)
{
   int pcount;
   NOTE_DATA *plog;
   pcount = 0;
   for( plog = pproject->first_log; plog; plog = plog->next )
   {
      pcount++;
      if(pcount == pnum)
         return plog;
   }
   return NULL;
}

void note_attach( ch ) CHAR_DATA *ch;
{
    NOTE_DATA *pnote;

    if ( ch->pnote != NULL )
	return;

    if ( note_free == NULL )
    {
	pnote     = alloc_perm( sizeof(*ch->pnote) );
    }
    else
    {
	pnote     = note_free;
	note_free = note_free->next;
    }

    pnote->next         = NULL;
    pnote->sender       = str_dup( ch->name );
    pnote->date         = str_dup( "" );
    pnote->to_list      = str_dup( "" );
    pnote->subject      = str_dup( "" );
    pnote->text         = str_dup( "" );
    ch->pnote           = pnote;
    return;
}
void write_projects()
{
    PROJECT_DATA *project;
    NOTE_DATA *log;
    FILE *fpout;
  
    fpout = fopen( PROJECTS_FILE, "w" );
    if ( !fpout )
    {
        bug( "FATAL: cannot open projects.txt for writing!\n\r", 0 );
        return;
    }
    for ( project = first_project; project; project = project->next )
    {
	fprintf( fpout, "Name		   %s~\n",  project->name );
       	fprintf( fpout, "Owner		   %s~\n",  (project->owner) ?
			project->owner : "None" );
	if ( project->coder )
	 fprintf( fpout, "Coder		    %s~\n", project->coder );
	fprintf( fpout, "Status		   %s~\n",  (project->status) ?
			project->status : "No update." );
 	fprintf( fpout, "Date		   %s~\n",  (project->date) ? 
			project->date : "Not Set?!?" );
	if ( project->description )
	  fprintf( fpout, "Description         %s~\n", 
				strip_cr(project->description) );
	if( project->first_log )
		for( log = project->first_log; log; log = log->next )
		  fprintf( fpout, "Log\n"
				  "Sender %s~\n"
				  "Date %s~\n"
				  "Subject %s~\n"
			          "Text %s~\n"
				  "Endlog\n",
	                log->sender,
	                log->date,
	                log->subject,
	                log->text
	                );
    
        fprintf( fpout, "End\n" );
    }
    fclose( fpout );
}