daleken/
daleken/data/notes/
daleken/data/player/
daleken/data/system/poses/
daleken/doc/Homepage/images/
daleken/log/
/*___________________________________________________________________________*
   )()(			  DalekenMUD 1.12 (C) 2000			)()(
   `]['		       by Martin Thomson, Lee Brooks,			`]['
    ||		       Ken Herbert and David Jacques			 ||
    || ----------------------------------------------------------------- ||
    || Envy Diku Mud improvements copyright (C) 1994 by Michael Quan,	 ||
    || David Love, Guilherme 'Willie' Arnold, and Mitchell Tse.		 ||
    || Merc Diku Mud improvments copyright (C) 1992, 1993 by Michael	 ||
    || Chastain, Michael Quan, and Mitchell Tse.			 ||
    || Original Diku Mud copyright (C) 1990, 1991			 ||
    || by Sebastian Hammer, Michael Seifert, Hans Henrik St{rfeldt,	 ||
    || Tom Madsen, and Katja Nyboe.					 ||
    || ----------------------------------------------------------------- ||
    || Any use of this software must follow the licenses of the		 ||
    || creators.  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.					 ||
    || ----------------------------------------------------------------- ||
    ||                              note.c                               ||
    || Note writing and notice board code.                               ||
 *_/<>\_________________________________________________________________/<>\_*/


#include "mud.h"
#include "db.h"

bool is_note_to		args( ( CHAR_DATA *ch, NOTE_DATA * pnote ) );
void note_attach	args( ( CHAR_DATA *ch ) );
void note_remove	args( ( CHAR_DATA *ch, NOTE_DATA * pnote ) );
void note_show		args( ( CHAR_DATA *ch, NOTE_DATA * pnote, int vnum ) );
void save_board		args( ( BOARD_DATA *board ) );


struct	board_data	board_table	[MAX_BOARD]	=
{
    {
	"general",	"General discussion board",	0,	2,
	"all",			"",				21
    },
    {
	"personal",	"Personal messages",		0,	1,
	"",			"all",				28
    },
    {
	"immortal",	"Messages from Immortals",	0,  L_APP,
	"",			"",				60
    },
    {
	"quest",	"Questing announcements/info",	0,     50,
	"",			"",				21
    },
    {
	"religion",	"Religion messages",		0,	5,
	"religion",		"all",				21
    },
    {
	"clan",		"Clan messages",		0,	5,
	"clan order guild",	"all",				21
    },
    {
	"ideas",	"Suggestions for improvements",	0,	2,
	"",			"",				60
    },
    {
	"bugs",		"Typos, bugs, errors",		0,	1,
	"",			"",				60
    }
};


/*
 * Find a board number based on a string.
 */
int board_lookup( const char *name )
{
    int i;

    for( i = 0; i < MAX_BOARD; i++ )
	if( !str_prefix( name, board_table[i].short_name ) )
	    return i;

    return -1;
}


/*
 * Find the number of a board.
 */
int board_number( const BOARD_DATA *board )
{
    int i;

    for ( i = 0; i < MAX_BOARD; i++ )
	if ( board == &board_table[i] )
	    return i;

    return -1;
}


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

    if( ch->pcdata->note )
	return;

    if( !note_free )
    {
	pnote	  = alloc_perm( sizeof( *ch->pcdata->note ) );
    }
    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( "" );
    pnote->expire	= 0;
    ch->pcdata->note	= pnote;
    return;
}


/*
 * Recycle a note.
 */
void free_note( NOTE_DATA *pnote )
{
    if( pnote->sender )
	free_string( pnote->sender );
    if( pnote->to_list )
	free_string( pnote->to_list );
    if( pnote->subject )
	free_string( pnote->subject );
    if( pnote->date )
	free_string( pnote->date );
    if( pnote->text )
	free_string( pnote->text );

    pnote->next	= note_free;
    note_free	= pnote;
    return;
}


/*
 * Remove list from the list.  Do not free note.
 */
void unlink_note( BOARD_DATA *board, NOTE_DATA *note )
{
    NOTE_DATA *p;

    if( board->note_first == note )
	board->note_first = note->next;
    else
    {
	for( p = board->note_first; p && p->next != note; p = p->next )
	    ;

	if( !p )
	    bug( "Unlink_note: couldn't find note." );
	else
	    p->next = note->next;
    }

    return;
}


void note_remove( CHAR_DATA *ch, NOTE_DATA *pnote )
{
    BOARD_DATA *board;
    NOTE_DATA  *prev;
    const char       *to_list;
    char	to_new	[ MAX_INPUT_LENGTH ];
    char	to_one	[ MAX_INPUT_LENGTH ];

    /*
     * Build a new to_list.
     * Strip out this recipient.
     */
    to_new[0]	= '\0';
    to_list	= pnote->to_list;
    while( *to_list != '\0' )
    {
	to_list	= one_argument( to_list, to_one );
	if( to_one[0] != '\0' && str_cmp( ch->name, to_one ) )
	{
	    strcat( to_new, " "	   );
	    strcat( to_new, to_one );
	}
    }

    /*
     * Just a simple recipient removal?
     */
    if( str_cmp( ch->name, pnote->sender ) && to_new[0] != '\0' )
    {
	free_string( pnote->to_list );
	pnote->to_list = str_dup( to_new + 1 );
	return;
    }

    board = ch->pcdata->board;

    /*
     * Remove note from linked list.
     */
    if( pnote == board->note_first )
    {
	board->note_first = pnote->next;
    }
    else
    {
	for( prev = board->note_first; prev; prev = prev->next )
	{
	    if ( prev->next == pnote )
		break;
	}

	if( !prev )
	{
	    bug( "Note_remove: pnote not found." );
	    return;
	}

	prev->next = pnote->next;
    }

    free_note( pnote );

    save_board( board );
    return;
}


bool is_note_to( CHAR_DATA *ch, NOTE_DATA *pnote )
{
    const char *argument;
    char word[MAX_INPUT_LENGTH];

    if( !str_cmp( ch->name, pnote->sender ) )
	return TRUE;

    argument = pnote->to_list;
    while( argument[0] != '\0' )
    {
	argument = one_argument( argument, word );

	if( word[0] == '!' && !str_cmp( ch->name, &word[1] )  )
	    return FALSE;
	if( !str_cmp( "all", word ) || !str_cmp( ch->name, word ) )
	    return TRUE;

	if( IS_IMMORTAL( ch ) && ( !str_cmp( "immortal", word )
				   || !str_cmp( "immortals", word )
				   || !str_cmp( "imm", word )
				   || !str_cmp( "immort", word ) ) )
	    return TRUE;
	if( IS_NPC( ch ) )
	    continue;
	if( !str_cmp( word, "religion" ) && ch->pcdata->religion )
	    return TRUE;
	if( ( !str_cmp( word, "clan" ) || !str_cmp( word, "clan-all" ) )
	    && ch->pcdata->clan
	    && ch->pcdata->clan->clan_type == CLAN_NORMAL )
	    return TRUE;
	if( ( !str_cmp( word, "guild" ) || !str_cmp( word, "guild-all" ) )
	      && ch->pcdata->clan
	    && ch->pcdata->clan->clan_type == CLAN_GUILD )
	    return TRUE;
	if( ( !str_cmp( word, "order" ) || !str_cmp( word, "order-all" ) )
	    && ch->pcdata->clan
	    && ch->pcdata->clan->clan_type == CLAN_ORDER )
	    return TRUE;
	if( !str_prefix( "religion-", word ) && ch->pcdata->religion
	    && !str_cmp( &word[9], ch->pcdata->religion->name ) )
	    return TRUE;
	else if( !str_prefix( "clan-", word ) && ch->pcdata->clan
	    && !str_cmp( &word[5], ch->pcdata->religion->name ) )
	    return TRUE;

	if( is_number( word )
	    && get_trust( ch ) >= atoi( word ) )
	    return TRUE;
    }

    return FALSE;
}


/*
 * Construct a clan list depending on who the note is targeted at.
 */
void note_target( CHAR_DATA *ch )
{
    const char *argument;
    char word[MAX_INPUT_LENGTH];
    char buf[MAX_STRING_LENGTH];

    buf[0] = '\0';
    argument = ch->pcdata->note->to_list;
    while( argument[0] != '\0' )
    {
	argument = one_argument( argument, word );

	strcat( buf, " " );
	strcat( buf, word );
	if( !str_cmp( word, "religion" ) && ch->pcdata->religion )
	{
	    strcat( buf, "-" );
	    strcat( buf, ch->pcdata->religion->name );
	}
	else if( !str_cmp( word, "clan" ) && ch->pcdata->clan )
	{
	    strcat( buf, "-" );
	    strcat( buf, ch->pcdata->religion->name );
	}
    }
    free_string( ch->pcdata->note->to_list );
    ch->pcdata->note->to_list = str_dup( &buf[1] );
}


int unread_notes( CHAR_DATA *ch, BOARD_DATA *board )
{
    NOTE_DATA *note;
    time_t     last_read;
    int	       count = 0;

    if( board->read_level > get_trust( ch ) )
	return -1;

    last_read = ch->pcdata->last_note[ board_number( board ) ];

    for( note = board->note_first; note; note = note->next )
	if( is_note_to( ch, note ) && last_read < note->date_stamp )
	    count++;

    return count;
}


int total_notes( CHAR_DATA *ch, BOARD_DATA *board )
{
    NOTE_DATA *note;
    int	       count = 0;

    if( board->read_level > get_trust( ch ) )
	return -1;

    for( note = board->note_first; note; note = note->next )
	if( is_note_to( ch, note ) )
	    count++;

    return count;
}


bool next_board( CHAR_DATA *ch )
{
    int i = board_number( ch->pcdata->board ) + 1;

    while( i < MAX_BOARD && unread_notes( ch, &board_table[i] ) == -1 )
	i++;

    if( i == MAX_BOARD )
	return FALSE;

    ch->pcdata->board = &board_table[i];
    return TRUE;
}


void note_show( CHAR_DATA *ch, NOTE_DATA * pnote, int vnum )
{
    char buf1[MAX_STRING_LENGTH * 2];
    char buf[MAX_STRING_LENGTH];
    time_t *last_note = &ch->pcdata->last_note[board_number(ch->pcdata->board)];

    strcpy( buf1, LINE_SEPARATOR );
    sprintf( buf, "&b[&c%3d&b]	&bTo: &c%s\n\r",
	     vnum, pnote->to_list );
    strcat( buf1, buf );
    sprintf( buf, "&bDate: &c%s&b  &bSender: &c%s\n\r",
	     pnote->date, pnote->sender );
    strcat( buf1, buf );
    sprintf( buf, "&rSubject: &y%s\n\r",
	     pnote->subject );
    strcat( buf1, buf );
    strcat( buf1, LINE_SEPARATOR );
    strcat( buf1, pnote->text );
    strcat( buf1, "&n" );
    send_to_char( buf1, ch );
    *last_note = UMAX( *last_note, pnote->date_stamp );
}


void do_board( CHAR_DATA *ch, const char *argument )
{
    int	 i;
    int	 count;
    char buf	[ MAX_INPUT_LENGTH ];

    if( IS_NPC( ch ) )
	return;

    if( argument[0] == '\0' )
    {
	int unread;

	count = 1;
	send_to_char( "\n\r&B-----------------------------[ &WBULLETIN "
		      "BOARDS&B ]-------------------------------&x\n\r", ch );
	for ( i = 0; i < MAX_BOARD; i++ )
	{
	    unread = unread_notes( ch, &board_table[i] );

	    if( unread == -1 )
		continue;

	    sprintf( buf, "&g[%2d] %c%c %s%4d/%4d  &c%s%-12s&g - %s\n\r",
		     count,
		     ( board_table[i].read_level  <= get_trust( ch ) )
		     ? 'R' : ' ',
		     ( board_table[i].write_level <= get_trust( ch ) )
		     ? 'W' : ' ',
		     ( unread == 0 ) ? "&g" : "&r",
		     unread, total_notes( ch, &board_table[i] ),
		     ( !strcmp( ch->pcdata->board->short_name,
				board_table[i].short_name ) ) ? "&B*&c" : " ",
		     board_table[i].short_name, board_table[i].long_name );
	    send_to_char( buf, ch );

	    count++;
	}

	send_to_char( LINE_SEPARATOR, ch );

	return;
    }

    i = atoi( argument ) - 1;

    if( is_number( argument ) )
    {
	if( i < 0 || i >= MAX_BOARD )
	{
	    send_to_char( "No such board.\n\r", ch );
	    return;
	}
    }
    else
    {
	if( ( i = board_lookup( argument ) ) == -1 )
	{
	    send_to_char( "No such board.\n\r", ch );
	    return;
	}
    }

    if( board_table[i].read_level > get_trust( ch ) )
    {
	send_to_char( "No such board.\n\r", ch );
	return;
    }

    ch->pcdata->board = &board_table[i];

    sprintf( buf, "&gCurrent board changed to &c%s&g. You can %s here.\n\r",
	     board_table[i].short_name,
	     ( get_trust( ch ) < board_table[i].write_level )
	     ? "only read" : "both read and write" );
    send_to_char( buf, ch );
    return;
}


/*
 * Date stamp idea comes from Alander of ROM
 */
void do_note( CHAR_DATA *ch, const char *argument )
{
    NOTE_DATA *pnote;
    BOARD_DATA *board;
    char buf[MAX_STRING_LENGTH];
    char arg[MAX_INPUT_LENGTH];
    int vnum;
    int anum;
    time_t *last_note;

    if( IS_NPC( ch ) )
	return;

    argument = one_argument( argument, arg );

    if( arg[0] == '\0' )
	strcpy( arg, "read" );

    board	=  ch->pcdata->board;
    last_note	= &ch->pcdata->last_note[board_number(board)];

    if( !str_cmp( arg, "list" ) )
    {
	char buf1[MAX_STRING_LENGTH * 2];

	anum = 0;
	vnum = 0;
	sprintf( buf1, "&gListing of the %s board:\n\r", board->short_name );
	strcat( buf1, LINE_SEPARATOR );
	for( pnote = board->note_first; pnote; pnote = pnote->next )
	{
	    vnum++;
	    if( is_note_to( ch, pnote ) )
	    {
		anum++;
		sprintf( buf, "&b[&c%3d%s&b] &r%s: &y%s&n\n\r",
			 vnum,
			 ( pnote->date_stamp > *last_note
			   && str_cmp( pnote->sender, ch->name ) ) ? "&rN" : " ",
			 pnote->sender, pnote->subject );
		strcat( buf1, buf );
	    }
	}
	sprintf( buf, "%d/%d visible notes on this board.&n\n\r",
		 anum, vnum );
	strcat( buf1, buf );
	send_to_char( buf1, ch );
	return;
    }

    if( !str_cmp( arg, "read" ) )
    {
	bool fAll;

	if( !str_cmp( argument, "all" ) )
	{
	    fAll = TRUE;
	    anum = 0;
	}
	else if( argument[0] == '\0' || !str_prefix( argument, "next" ) )
	    /* read next unread note */
	{
	    vnum = 0;
	    for( pnote = board->note_first; pnote; pnote = pnote->next )
	    {
		vnum++;
		if( is_note_to( ch, pnote )
		    && str_cmp( ch->name, pnote->sender )
		    && *last_note < pnote->date_stamp )
		    break;
	    }
	    if( !pnote )	/* Ensure they are caught up */
	    {
		*last_note = current_time;
	    }
	    if( pnote )
	    {
		note_show( ch, pnote, vnum );
		return;
	    }

	    send_to_char( "&gNo new notes in this board", ch );

	    if( next_board( ch ) )
		charprintf( ch, "; changed to &c%s&g board",
			    ch->pcdata->board->short_name );
	    send_to_char( ".&n\n\r", ch );
	    return;
	}
	else if( is_number( argument ) )
	{
	    fAll = FALSE;
	    anum = atoi( argument );
	}
	else
	{
	    send_to_char( "Note read which number?\n\r", ch );
	    return;
	}

	vnum = 0;
	for( pnote = board->note_first; pnote; pnote = pnote->next )
	{
	    vnum++;
	    if( is_note_to( ch, pnote ) )
	    {
		if( fAll )
		{
		    *last_note = UMAX( *last_note, pnote->date_stamp );
		}
		else if( vnum == anum )
		{
		    note_show( ch, pnote, vnum );
		    return;
		}
	    }
	}

	if( fAll )
	    send_to_char( "&gAll notes caught up.&n\n\r", ch );
	else
	    send_to_char( "No such note.\n\r", ch );
	return;
    }

    if( !str_cmp( arg, "write" ) || !str_cmp( arg, "edit" ) )
    {
	note_attach( ch );
	string_edit( ch, &ch->pcdata->note->text );
	return;
    }

    if( !str_cmp( arg, "expire" ) && IS_IMMORTAL( ch ) )
    {
	if( !is_number( argument ) )
	{
	    send_to_char( "Note expire which number of days?\n\r", ch );
	    return;
	}

	anum = atoi( argument );

	ch->pcdata->note->expire = current_time + anum * 24 * 3600;
	send_to_char( "Expiry time set.\n\r", ch );
	return;
    }

    if( !str_cmp( arg, "+" ) )
    {
	note_attach( ch );
	strcpy( buf, ch->pcdata->note->text );
	if( strlen( buf ) + strlen( argument ) >= MAX_STRING_LENGTH - 200 )
	{
	    send_to_char( "Note too long.\n\r", ch );
	    return;
	}

	strcat( buf, argument );
	strcat( buf, "\n\r" );
	free_string( ch->pcdata->note->text );
	ch->pcdata->note->text = str_dup( buf );
	send_to_char( "Line appended to note.\n\r", ch );
	return;
    }

    if( !str_cmp( arg, "subject" ) )
    {
	note_attach( ch );
	free_string( ch->pcdata->note->subject );
	ch->pcdata->note->subject = str_dup( argument );
	send_to_char( "Subject set.\n\r", ch );
	return;
    }

    if( !str_cmp( arg, "to" ) )
    {
	note_attach( ch );
	free_string( ch->pcdata->note->to_list );
	ch->pcdata->note->to_list = str_dup( argument );
	send_to_char( "Note recipient(s) set.\n\r", ch );
	return;
    }

    if( !str_cmp( arg, "catchup" ) )
    {
	if( get_trust( ch ) < ch->pcdata->board->write_level )
	{
	    send_to_char( "You can't ignore the notes on this board.\n\r", ch );
	    return;
	}
	*last_note = current_time;
	send_to_char( "All messages skipped.\n\r", ch );
	return;
    }

    if( !str_cmp( arg, "clear" ) )
    {
	if ( ch->pcdata->note )
	{
	    free_note( ch->pcdata->note );
	    ch->pcdata->note	= NULL;
	}

	send_to_char( "Note cleared.\n\r", ch );
	return;
    }

    if( !str_cmp( arg, "show" ) )
    {
	if( !ch->pcdata->note )
	{
	    send_to_char( "You have no note in progress.\n\r", ch );
	    return;
	}

	free_string( ch->pcdata->note->date );
	ch->pcdata->note->date = str_dup( "now" );
	note_show( ch, ch->pcdata->note, 0 );
	free_string( ch->pcdata->note->date );
	ch->pcdata->note->date = str_dup( "" );
	return;
    }

    if( !str_cmp( arg, "post" ) || !str_prefix( arg, "send" ) )
    {
	FILE *fp;
	char strsave [ MAX_INPUT_LENGTH ];

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

	if( get_trust( ch ) < ch->pcdata->board->write_level )
	{
	    send_to_char( "You can't post notes on this board.\n\r", ch );
	    return;
	}

	if( ch->pcdata->note->to_list[0] == '\0' )
	{
	    free_string( ch->pcdata->note->to_list );
	    if( board->include[0] != '\0' )
	    {
		ch->pcdata->note->to_list = str_dup( board->include );
	    }
	    else
	    {
		ch->pcdata->note->to_list = str_dup( "all" );
	    }
	    charprintf( ch, "Default recipient '&g%s&n' chosen.\n\r",
			ch->pcdata->note->to_list );
	}
	if( board->include[0] != '\0' )
	{
	    if( !is_name( board->include, ch->pcdata->note->to_list ) )
	    {
		sprintf( buf, "%s %s", ch->pcdata->note->to_list,
			 board->include );
		free_string( ch->pcdata->note->to_list );
		ch->pcdata->note->to_list = str_dup( buf );

		charprintf( ch, "Included mandatory recipient(s) '&g%s&n'.\n\r",
			    board->include );
	    }
	}
	if( board->exclude[0] != '\0' )
	{
	    if( is_name( board->exclude, ch->pcdata->note->to_list ) )
	    {
		charprintf( ch, "The recipient may not include '&y%s&n'.\n\r",
			 board->exclude );
		return;
	    }
	}

	note_target( ch );

	if( !str_cmp( ch->pcdata->note->to_list, "" ) )
	{
	    send_to_char(
		"You need to provide a recipient (name, all, or immortal).\n\r",
		ch );
	    return;
	}

	if( !str_cmp( ch->pcdata->note->subject, "" ) )
	{
	    send_to_char( "You need to provide a subject.\n\r", ch );
	    return;
	}

	ch->pcdata->note->next		= NULL;
	ch->pcdata->note->date		= str_dup( myctime( &current_time ) );
	ch->pcdata->note->date_stamp	= current_time;
	if( !ch->pcdata->note->expire )
	    ch->pcdata->note->expire	= current_time +
		board->purge_days * 24 * 3600;

	if( !board->note_first )
	{
	    board->note_first	= ch->pcdata->note;
	}
	else
	{
	    for( pnote = board->note_first; pnote->next; pnote = pnote->next )
		;
	    pnote->next = ch->pcdata->note;
	}

	pnote = ch->pcdata->note;
	ch->pcdata->note = NULL;

	sprintf( strsave, "%s%s", NOTE_DIR, board->short_name );

	if ( !( fp = open_file( strsave, "a", FALSE ) ) )
	{
	    perror( board->short_name );
	}
	else
	{
	    write_next_item( fp, "Note", pnote );
	    close_file( fp );
	}

	{			/* notify everyone */
	    DESCRIPTOR_DATA *d;
	    char buf[MAX_STRING_LENGTH];

	    sprintf( buf, "&g'I have a note here from %s&g'.&n\n\r",
		     ch->name );

	    for( d = descriptor_list; d; d = d->next )
	    {
		if( is_note_to( CH( d ), pnote ) )
		{
		    send_to_char( "Just before your dog kills him,\n\r the postman says: ", d->character );
		    send_to_char( buf, d->character );
		}
	    }
	}
	charprintf( ch, "Note posted.  This note will expire %s.\n\r",
		    myctime( &pnote->expire ) );
	return;
    }

    if( !str_cmp( arg, "remove" ) )
    {
	if( !is_number( argument ) )
	{
	    send_to_char( "Note remove which number?\n\r", ch );
	    return;
	}

	anum = atoi( argument );
	vnum = 0;
	for( pnote = board->note_first; pnote; pnote = pnote->next )
	{
	    vnum++;
	    if( is_note_to( ch, pnote ) && vnum == anum )
	    {
		note_remove( ch, pnote );
		send_to_char( "Ok.\n\r", ch );
		return;
	    }
	}

	send_to_char( "No such note.\n\r", ch );
	return;
    }

    send_to_char( "Huh?	 Type 'help note' for usage.\n\r", ch );
    return;
}


void load_notes( )
{
    FILE *fpArch;
    NOTE_DATA *new_note, *pnotelast;
    char strsave[ MAX_INPUT_LENGTH ];
    int i, entry;

    for( i = 0; i < MAX_BOARD; i++ )
    {
	sprintf( strsave, "%s%s", NOTE_DIR, board_table[i].short_name );

	if( !( fpArea = open_file( strsave, "r", TRUE ) ) )
	{
	    bug( "Couldn't open board for reading" );
	    continue;
	}

	pnotelast = NULL;
	for( ;; )
	{
	    new_note = read_next_item( fpArea, &entry );
	    if( entry < 0 || !new_note )
		break;

	    if( new_note->expire < current_time )
	    {
		sprintf( strsave, "%s%s.old", NOTE_DIR, board_table[i].short_name );

		if( !( fpArch = open_file( strsave, "a", FALSE ) ) )
		    bug( "Load_notes: couldn't open arch boards for writing." );
		else
		{
		    write_next_item( fpArch, "Note", new_note );
		    close_file( fpArch );
		}

		free_note( new_note );
		board_table[i].changed = TRUE;
		continue;
	    }

	    if( !board_table[i].note_first )
		board_table[i].note_first   = new_note;
	    else
		pnotelast->next     = new_note;

	    pnotelast               = new_note;
	}
    }
    fpArea = NULL;
    strArea[0] = '\0';
}


void save_board( BOARD_DATA *board )
{
    FILE *fp;
    NOTE_DATA *note;
    char strsave[ MAX_INPUT_LENGTH ];

    sprintf( strsave, "%s%s", NOTE_DIR, board->short_name );

    if( !( fp = open_file( strsave, "w", TRUE ) ) )
	perror( board->short_name );
    else
    {
	for( note = board->note_first; note; note = note->next )
	    write_next_item( fp, "Note", note );
	close_file( fp );
    }
}


void save_notes( void )
{
    int i;

    for( i = 0; i < MAX_BOARD; i++ )
	save_board( &board_table[i] );
}