/**
 * Create a copy of a restart marker.
 * @ingroup globus_ftp_client_restart_marker
 *
 * This function copies the contents of marker to new_marker.
 *
 * @param new_marker
 *        A pointer to a new restart marker.
 * @param marker
 *        The marker to copy.
 *
 * @see globus_ftp_client_restart_marker_init(),
 * globus_ftp_client_restart_marker_destroy()
 */
globus_result_t
globus_ftp_client_restart_marker_copy(
    globus_ftp_client_restart_marker_t *	new_marker,
    globus_ftp_client_restart_marker_t *	marker)
{
    globus_fifo_t * tmp;
    GlobusFuncName(globus_ftp_client_restart_marker_copy);

    if(new_marker == GLOBUS_NULL)
    {
        return globus_error_put(
		GLOBUS_I_FTP_CLIENT_ERROR_NULL_PARAMETER("new_marker"));
    }
    if(marker == GLOBUS_NULL)
    {
        return globus_error_put(
		GLOBUS_I_FTP_CLIENT_ERROR_NULL_PARAMETER("marker"));
    }

    globus_ftp_client_restart_marker_init(new_marker);

    new_marker->type = marker->type;

    switch(new_marker->type)
    {
    case GLOBUS_FTP_CLIENT_RESTART_NONE:
	break;
    case GLOBUS_FTP_CLIENT_RESTART_STREAM:
	new_marker->stream.offset = marker->stream.offset;
	break;
    case GLOBUS_FTP_CLIENT_RESTART_EXTENDED_BLOCK:

	globus_fifo_init(&new_marker->extended_block.ranges);

	if(globus_fifo_empty(&marker->extended_block.ranges))
	{
	    break;
	}
	tmp = globus_fifo_copy(&marker->extended_block.ranges);

	while(!globus_fifo_empty(tmp))
	{
	    globus_i_ftp_client_range_t *	range;

	    range = (globus_i_ftp_client_range_t *) globus_fifo_dequeue(tmp);

	    globus_ftp_client_restart_marker_insert_range(new_marker,
							  range->offset,
							  range->end_offset);
	}
	
	globus_fifo_destroy(tmp);
	globus_free(tmp);
	break;
    }
    return GLOBUS_SUCCESS;
}
static
void
restart_marker_plugin_data_cb(
    globus_ftp_client_plugin_t *                plugin,
    void *                                      plugin_specific,
    globus_ftp_client_handle_t *                handle,
    globus_object_t *                           error,
    const globus_byte_t *                       buffer,
    globus_size_t                               length,
    globus_off_t                                offset,
    globus_bool_t                               eof)
{
    restart_marker_plugin_info_t *              ps;
    time_t                                      time_now;
    globus_result_t                             result;

    ps = (restart_marker_plugin_info_t *) plugin_specific;

    if(ps->marker_cb && ps->use_data && !error)
    {
        globus_mutex_lock(&ps->lock);

        time_now = time(NULL);

        result = globus_ftp_client_restart_marker_insert_range(
                &ps->restart_marker,
                offset,
                offset + length);

        if(result == GLOBUS_SUCCESS &&
            time_now > ps->last_time)
        {
            ps->last_time = time_now;

            ps->marker_cb(
                ps->user_arg,
                handle,
                &ps->restart_marker);
        }

        globus_mutex_unlock(&ps->lock);
    }
}
static
void
restart_marker_plugin_response_cb(
    globus_ftp_client_plugin_t *                plugin,
    void *                                      plugin_specific,
    globus_ftp_client_handle_t *                handle,
    const char *                                url,
    globus_object_t *                           error,
    const globus_ftp_control_response_t *       ftp_response)
{
    restart_marker_plugin_info_t *              ps;
    char *                                      p;
    globus_off_t                                offset;
    globus_off_t                                end;
    int                                         consumed;
    globus_result_t                             result;
    int                                         count;

    ps = (restart_marker_plugin_info_t *) plugin_specific;

    if(ps->marker_cb &&
        !error &&
        ftp_response &&
        ftp_response->response_buffer &&
        ftp_response->code == 111)
    {
        /* skip '111 Range Marker' */
        p = strstr((char *) ftp_response->response_buffer, "Range Marker");
        if(!p)
        {
            return;
        }

        p += sizeof("Range Marker");

        count = 0;
        while(sscanf(p, " %" GLOBUS_OFF_T_FORMAT " - %" GLOBUS_OFF_T_FORMAT " %n",
                &offset, &end, &consumed) >= 2)
        {
            result = globus_ftp_client_restart_marker_insert_range(
                &ps->restart_marker,
                offset,
                end);

            if(result == GLOBUS_SUCCESS)
            {
                count++;
            }
            else
            {
                break;
            }

            p += consumed;

            if(*p == ',')
            {
                p++;
            }
            else
            {
                break;
            }
        }

        if(count)
        {
            ps->marker_cb(
                ps->user_arg,
                handle,
                &ps->restart_marker);
        }
    }
}
int main(int argc,
	 char *argv[])
{
    globus_ftp_client_handle_t			handle;
    globus_ftp_client_operationattr_t		attr;
    globus_byte_t				buffer[SIZE];
    globus_size_t				buffer_length = sizeof(buffer);
    globus_result_t				result;
    char *					src;
    char *					dst;
    globus_ftp_client_handleattr_t		handle_attr;
    globus_ftp_control_layout_t			layout;
    globus_ftp_control_parallelism_t		parallelism;
    int						i;
    globus_ftp_client_restart_marker_t		restart;

    LTDL_SET_PRELOADED_SYMBOLS();
    globus_module_activate(GLOBUS_FTP_CLIENT_MODULE);
    globus_ftp_client_handleattr_init(&handle_attr);
    globus_ftp_client_operationattr_init(&attr);

    layout.mode = GLOBUS_FTP_CONTROL_STRIPING_NONE;
    parallelism.mode = GLOBUS_FTP_CONTROL_PARALLELISM_NONE;

    globus_ftp_client_restart_marker_init(&restart);

    /* Parse local arguments */
    for(i = 1; i < argc; i++)
    {
	if(strcmp(argv[i], "-S") == 0 && i < argc)
	{
	    layout.mode =
		GLOBUS_FTP_CONTROL_STRIPING_BLOCKED_ROUND_ROBIN;
	    layout.round_robin.block_size = 15;

	    test_remove_arg(&argc, argv, &i, 0);
	}
	else if(strcmp(argv[i], "-P") == 0 && i + 1 < argc)
	{
	    parallelism.mode = GLOBUS_FTP_CONTROL_PARALLELISM_FIXED;
	    parallelism.fixed.size = atoi(argv[i+1]);

	    test_remove_arg(&argc, argv, &i, 1);
	}
	else if(strcmp(argv[i], "-R") == 0 && i+1 < argc)
	{
	    char * p;
	    globus_off_t offset, end;
	    int bytes;

	    p = argv[i+1];
	    while((*p) && (sscanf(p,
				  "%"GLOBUS_OFF_T_FORMAT
				  "-%"GLOBUS_OFF_T_FORMAT"%n",
				  &offset,
				  &end,
				  &bytes) >= 2))
	    {
		globus_ftp_client_restart_marker_insert_range(
		    &restart,
		    offset, end);
		p += bytes;
		if(*p && *p == ',') p++;
	    }
	    test_remove_arg(&argc, argv, &i, 1);
	}
    }
    test_parse_args(argc, 
		    argv,
                    &handle_attr,
                    &attr,
		    &src,
		    &dst);

    globus_mutex_init(&lock, GLOBUS_NULL);
    globus_cond_init(&cond, GLOBUS_NULL);

    globus_ftp_client_operationattr_set_mode(
        &attr,
        GLOBUS_FTP_CONTROL_MODE_EXTENDED_BLOCK);
    globus_ftp_client_operationattr_set_layout(&attr,
	                                       &layout);
    globus_ftp_client_operationattr_set_parallelism(&attr,
					            &parallelism);

    globus_ftp_client_handle_init(&handle,  &handle_attr);

    done = GLOBUS_FALSE;
    result = globus_ftp_client_get(&handle,
				   src,
				   &attr,
				   &restart,
				   done_cb,
				   0);
    if(result != GLOBUS_SUCCESS)
    {
	fprintf(stderr, "%s", globus_object_printable_to_string(globus_error_get(result)));
	done = GLOBUS_TRUE;
	error = GLOBUS_TRUE;
    }
    else
    {
	globus_ftp_client_register_read(
	    &handle,
	    buffer,
	    buffer_length,
	    data_cb,
	    0);
    }
    globus_mutex_lock(&lock);
    while(!done)
    {
	globus_cond_wait(&cond, &lock);
    }
    globus_mutex_unlock(&lock);

    globus_ftp_client_handle_destroy(&handle);

    globus_module_deactivate_all();

    if(test_abort_count && error)
    {
	return 0;
    }
    return error;
}
/**
 * Initialize a restart marker from a string.
 * @ingroup globus_ftp_client_restart_marker
 *
 * This function initializes a new restart, @a marker, based on the
 * @a marker_string parameter. The string may be either a single offset
 * for a stream-mode restart marker, or a comma-separated list of start-end
 * ranges.
 *
 * @param marker
 *        The restart marker to be unitialized.
 * @param marker_string
 *        The string containing a textual representation of a restart marker.
 * @see globus_ftp_client_restart_marker
 */
globus_result_t
globus_ftp_client_restart_marker_from_string(
    globus_ftp_client_restart_marker_t *	marker,
    const char *				marker_string)
{
    globus_off_t				offset;
    globus_off_t				end;
    int						consumed;
    globus_object_t *				err;
    globus_result_t				res;
    const char *				p;
    GlobusFuncName(globus_ftp_client_restart_marker_from_string);

    if(marker == GLOBUS_NULL)
    {
        return globus_error_put(
	    GLOBUS_I_FTP_CLIENT_ERROR_NULL_PARAMETER("marker"));
    }
    else if(marker_string == GLOBUS_NULL)
    {
        return globus_error_put(
	    GLOBUS_I_FTP_CLIENT_ERROR_NULL_PARAMETER("marker_string"));
    }

    res = globus_ftp_client_restart_marker_init(marker);
    if(res != GLOBUS_SUCCESS)
    {
        goto res_exit;
    }

    if(strchr(marker_string, '-') != GLOBUS_NULL)
    {
        /* Looks like an extended block mode restart marker */
	if(marker->type == GLOBUS_FTP_CLIENT_RESTART_NONE)
	{
	    marker->type = GLOBUS_FTP_CLIENT_RESTART_EXTENDED_BLOCK;
	}
	if(marker->type != GLOBUS_FTP_CLIENT_RESTART_EXTENDED_BLOCK)
	{
	    err = GLOBUS_I_FTP_CLIENT_ERROR_INVALID_PARAMETER("marker");

	    goto error_exit;
	}

        p = marker_string;
	while( sscanf(p, "%"GLOBUS_OFF_T_FORMAT"-%"GLOBUS_OFF_T_FORMAT"%n",
	              &offset,
		      &end,
		      &consumed) >= 2)
	{
	    res = globus_ftp_client_restart_marker_insert_range(marker,
	                                                        offset,
								end);
	    if(res != GLOBUS_SUCCESS)
	    {
	        goto res_exit;
	    }

	    p += consumed;
	    if(*p == ',')
	    {
	        p++;
	    }
	    else
	    {
	        break;
	    }
	}
    }
    else /* assume stream mode */
    {
        if(marker->type == GLOBUS_FTP_CLIENT_RESTART_NONE)
	{
	    marker->type = GLOBUS_FTP_CLIENT_RESTART_STREAM;
	}
	if(marker->type != GLOBUS_FTP_CLIENT_RESTART_STREAM)
	{
	    err = GLOBUS_I_FTP_CLIENT_ERROR_INVALID_PARAMETER("marker");

	    goto error_exit;
	}
	if(sscanf(marker_string, "%"GLOBUS_OFF_T_FORMAT, &offset) != 1)
	{
	    err = GLOBUS_I_FTP_CLIENT_ERROR_INVALID_PARAMETER("marker_string");

	    goto error_exit;
	}
	else
	{
	    marker->stream.ascii_offset = marker->stream.offset = offset;
	}
    }

    return GLOBUS_SUCCESS;

  error_exit:
    res = globus_error_put(err);
  res_exit:
    return res;
}