/* -*- mode: c -*- */

/* Copyright (C) 2008-2016 Alexander Chernov <cher@ejudge.ru> */

/*
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 2 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 */

#include "ejudge/config.h"

#include <stdio.h>

#if HAVE_FMEMOPEN - 0 == 0

#include <stdlib.h>

static void addINode(int i_stream_number, FILE *file);
static void delINode(FILE *file);
static int get_i_stream_number(void);

struct iListNode
{
  int i_stream_number;
  FILE* file;
  struct iListNode *pnext;
};

static struct iListNode *iList = NULL;

static void
addINode(int i_stream_number, FILE *file)
{
  struct iListNode **pcur = &iList;
  struct iListNode *node = calloc(1, sizeof(struct iListNode));
  if(node == NULL)
    abort();

  while((*pcur) && (*pcur)->i_stream_number < i_stream_number)
    pcur = &((*pcur)->pnext);

  node->pnext = *pcur;
  node->i_stream_number = i_stream_number;
  node->file = file;
  (*pcur) = node;
}

static void
delINode(FILE *file)
{
  struct iListNode **pcur = &iList;
  struct iListNode *todel;
  char file_name[30];

  while((*pcur) && (*pcur)->file != file)
    pcur = &((*pcur)->pnext);

  todel = (*pcur);
  if(todel == NULL){ //not found
    // WARNING: (("Trying to close a simple FILE* with fmemclose()"));
  } else {
    sprintf(file_name,"i_stream_%d",todel->i_stream_number);
    remove(file_name);

    (*pcur) = todel->pnext;
    free(todel);
  }
}

static int
get_i_stream_number(void)
{
  int i_stream_number = 1;
  struct iListNode *cur = iList;

  while(cur && i_stream_number >= cur->i_stream_number){
    i_stream_number++;
    cur = cur->pnext;
  }
  return i_stream_number;
}

FILE *
fmemopen(void *buf, size_t size, const char *mode)
{
  FILE *f;
  char file_name[30];
  int i_stream_number;

  i_stream_number = get_i_stream_number();
  sprintf(file_name,"i_stream_%d",i_stream_number);
  f = fopen(file_name,"w+");

  if(!f)
    return NULL;

  if(size != fwrite(buf, 1, size, f)){
    fclose(f);
    remove(file_name);
    return NULL;
  }
  if(EOF == fseek(f, 0, SEEK_SET)){
    fclose(f);
    remove(file_name);
    return NULL;
  }

  addINode(i_stream_number, f);

  return f;
}

void
fmemclose(FILE *f)
{
  delINode(f);
  fclose(f);
}

#else

void
fmemclose(FILE *f)
{
  fclose(f);
}

#endif
