/* Begin of shm_cat.c */

#include <stdio.h>
#include <string.h>
#include <ctype.h>

#include "shm_pack.h"

static void shmDump(SMEM *smd)
{
  char Buffer[128];

  while(!shm_eosm(smd))
  {
    int nBytes;

    nBytes = shm_read(Buffer,1,128,smd);
    fwrite(Buffer,1,nBytes,stdout);
  }
}

void main(int argc, char *argv[])
{
  int    i;
  SMEM *smd;

  if (argc == 1)
  { fprintf(stderr,"Usage: %s <key> ... \n",argv[0]);
    exit(-1);
  }

  for(i=1; i<argc; i++)
  {
    int    key;
    SMEM *smd;

    key = atoi(argv[i]);
    smd = shm_access(key,0); /* 0 = Read only */

    if (smd == NULL)
      fprintf(stderr,"No accessible shared memory with key %d\n",key);
    else
      shmDump(smd);
  }
}

/* End of shm_cat.c */
/* Begin of shm_cp.c */

#include <stdio.h>
#include "shm_pack.h"

void  main(int argc, char *argv[])
{
  int    i,key;
  SMEM   *src, *dest;

  if (argc != 3)
  { fprintf(stderr,"Usage: %s <src_key> <dest_key> \n",argv[0]);
    exit(-1);
  }

  /* Check if <src_key> exists... */
  key = atoi(argv[1]);
  src = shm_access(key,0);
  if (src == NULL)
  { fprintf(stderr,"shm_cp: %d non-existent\n",key);
    exit(-1);
  }

  /* ...and <dest_key> does not*/
  key = atoi(argv[2]);
  dest = shm_access(key,0);
  if (dest != NULL)
  { fprintf(stderr,"shm_cp: cannot overwrite %d \n",key);
    exit(-1);
  }

  /* Creating <dest_key> */
  dest = shm_create(key,"rw",src->size);

  /* Copying <src_key> into <dest_key> */
  while(!shm_eosm(src))
  {
    int nBytes;
    char Buffer[BUFSIZ];

    nBytes = shm_read(Buffer,1,BUFSIZ,src);
    shm_write(Buffer,1,nBytes,dest);
  }
}

/* End of shm_cp.c */
/* Begin of shm_load.c */

#include <stdio.h>
#include <string.h>
#include <sys/stat.h>

#include "shm_pack.h"

static int header_size = 0;

/**  Load a binary file in a shared memory
 **  copy the contains of the file in the shared memory
 **/
static void load_file(FILE *fd, SMEM *smd)
{
  char buffer[BUFSIZ];
  int  n_item;

  /* Don't load the header, if any */
  if (header_size)
    fseek(fd,header_size,SEEK_SET);

  while (!feof(fd))
  {
    n_item = fread(buffer,1,BUFSIZ,fd);
    shm_write(buffer,1,n_item,smd);
  }

}

void main(int argc, char *argv[])
{
  int    size, key;
  FILE   *fd;
  SMEM   *smd;
  struct stat statbuf;

  if ((argc != 3) && (argc != 4))
  { fprintf(stderr,"Usage: %s <filename> <key> [-header=n] \n",argv[0]);
    exit(-1);
  }

  fd = fopen(argv[1],"r");
  if (!fd)
  {
    fprintf(stderr,"Abort: %s not found\n",argv[1]);
    exit(-1);
  }

  fstat(fileno(fd),&statbuf);
  size = statbuf.st_size;

  key = atoi(argv[2]);
  smd = shm_access(key,0);
  if (smd != NULL)
  {
    fprintf(stderr,"Abort: key %d already exists\n",key);
    exit(-1);
  }

  if (argc == 4)
  {
    char *header;

    header = argv[3];
    if ((header[0] != '-') || (header[1] != 'h'))
    {
      fprintf(stderr,"Abort: third option: -header=n\n");
      exit(-1);
    }

    header = strchr(argv[3],'=');
    if (!header)
    {
      fprintf(stderr,"Abort: third option: -header=n\n");
      exit(-1);
    }

    header_size = atoi(&header[1]);
  }

  size -= header_size;
  smd = shm_create(key,"rw",size); /* create shared memory */

  if (smd == NULL)
  {
    fprintf(stderr,"Abort: cannot create %d\n",key);
    exit(-1);
  }

  load_file(fd,smd);
}

/* End of shm_load.c */
/* Begin of shm_ls.c */

#include <stdio.h>
#include <stdlib.h>
#include <time.h>

#include "shm_pack.h"

static char *execute(char *command)
{
 static char targetFile[L_tmpnam];
 char cLine[64];

 /* Generate temporary filename */
 tmpnam(targetFile);

 /* Build the command line */
 sprintf(cLine,"%s > %s\n",command,targetFile);

 /* Perform the system call */
 if (0 != system(cLine))
   return NULL;

 return targetFile;
}

static void smPrint(int key)
{
 SMEM   *smd;
 char   strTime[32];

 smd = shm_access(key,0); /* 0 = Read Only */
 if (smd == NULL)
 {
   printf("%38s\n","<No Access>");
   return;
 }

 strcpy(strTime,ctime(&smd->time));
 strTime[16] = '\0';
 printf("  %10d %s %12d\n",smd->size,&strTime[4],smd->key);

 shm_close(smd);
}

void main()
{
 FILE *fd;
 char *filename;
 char line[128];

 filename = execute("ipcs");
 fd = fopen(filename,"r");
 if (fd == NULL)
    return;

 while(!feof(fd))
 {
   fgets(line,80,fd);
   if (line[0] == 'm')
   {
     int key;

     line[19] = '\0';
     line[49] = '\0';
     printf("%s",&line[20]);
     sscanf(&line[9],"0x%x",&key);
     smPrint(key);
   }
 }

 fclose(fd);
 unlink(filename);
}

/* End of shm_ls.c */
/* Begin of shm_pack.c */

/**
 **  Description:
 **    shm_pack is a package which manages shared memories like a
 **    virtual file. It makes easier the manipulation of shared memories
 **    for any programmer who is familiar with file manipulation.
 **    It provides some protections to avoid to memory corrumption.
 **
 **/

#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/shm.h>
#include <sys/stat.h>
#include <errno.h>
#include <stdio.h>
#include <string.h>
#include <time.h>

#include "shm_pack.h"

#define READ_ONLY 0

/* Create a shared memory [cf: fopen(file,"w") ]
 *
 * shm_create(80,"",65536) :
 *   create a 64k shared-memory at key 80 (only owner can access)
 *
 * shm_create(80,"r",65536) :
 *   idem, but other than owner can read
 *
 * shm_create(80,"rw",65536) :
 *   idem, but other than owner can read & write
 */
SMEM *shm_create(int key, const char *mode, int size)
{
  int   addr;
  int   shmid;
  SMEM *smd = NULL;
  int   bCreate;
  struct shmid_ds shmid_d;
  int Permission;

  /* Default permission : only owner has right to read/write */
  Permission = IPC_CREAT | S_IRUSR | S_IWUSR;

  /* Search for read permission */
  if (strchr(mode,'r'))
    Permission |= S_IRGRP | S_IROTH;

  /* Search for write permission */
  if (strchr(mode,'w'))
    Permission |= S_IWGRP | S_IWOTH;

  /* Try to create shared memory */
  shmid = shmget(key,size,Permission);
  if (shmid < 0)
    return NULL;

  /* Try to attach with it */
  addr = (int)shmat(shmid,0,0);
  if (addr <= 0)
    return NULL;

  /* Set time */
  if (-1 == shmctl(shmid,IPC_STAT,&shmid_d))
    return NULL;

  shmid_d.shm_ctime = time(NULL);
  shmctl(shmid,IPC_SET,&shmid_d);

  /* Allocate & set the shared memory descriptor */
  smd = (SMEM *)malloc(sizeof(SMEM));
  smd->addr   = (void *)addr;
  smd->size   = size;
  smd->mode   = 1;
  smd->offset = 0;
  return smd;
}

/* Access a already-exist shared memory, either in read or
 * write mode. [cf: fopen(file,"r") ]
 */
SMEM *shm_access(int key, int mode)
{
  int   addr;
  int   shmid;
  SMEM *smd = NULL;
  int   bCreate;
  struct shmid_ds shmid_d;

  /* Try to access shared memory */
  shmid = shmget(key,0,0);
  if (shmid < 0)
    return NULL;

  /* Try to attach with it */
  addr = (int)shmat(shmid,0,0200);
  if (addr < 0)
    return NULL;

  /* Allocate & set the shared memory descriptor */
  smd = (SMEM *)malloc(sizeof(SMEM));
  smd->key    = key;
  smd->addr   = (void *)addr;
  smd->mode   = mode;
  smd->offset = 0;

  if (-1 == shmctl(shmid,IPC_STAT,&shmid_d))
    return NULL;

  smd->size = shmid_d.shm_segsz;
  smd->time = shmid_d.shm_ctime;
  return smd;
}

/* Read 'nElem' elements of 'nSize' size from shared memory [cf: fread() ] */
int   shm_read(void *ptr, int nSize, int nElem, SMEM *smd)
{
 char *cpRight;
 char *cpLeft;
 int  nBytes;
 int i;
 int shmid;

 /* Return error if shared memory doesn't exist */
 shmid = shmget(smd->key,0,0);
 if (shmid < 0)
   return -1;

 /* If End-Of-Shared-Memory is reached, return 0 bytes read */
 if (smd->offset > smd->size)
   return 0;

 /* How many bytes to read */
 nBytes = nSize * nElem;

 /* Does not exceed End-Of-Shared-Memory */
 if ((smd->offset + nBytes) > smd->size)
   nBytes = (smd->size - smd->offset);

 /* left and right arguments */
 cpLeft  =  (char *)ptr;
 cpRight =  (char *)(smd->addr);
 cpRight += (smd->offset);

 /* Read the bytes (use memcpy) */
 for(i=0; i<nBytes; i++)
   cpLeft[i] = cpRight[i];

 /* Set smd->offset and return number of elements read */
 smd->offset += nBytes;
 return (nBytes/nSize);
}

/* Write 'nElem' elements of 'nSize' size into shared memory [cf: fwrite() ] */
int   shm_write(void *ptr, int nSize, int nElem, SMEM *smd)
{
 char *cpRight;
 char *cpLeft;
 int i;
 int nBytes;
 int shmid;

 /* Return error if shared memory doesn't exist */
 shmid = shmget(smd->key,0,0);
 if (shmid < 0)
   return -1;

 /* Prevent writing if READ_ONLY */
 if (smd->mode == READ_ONLY)
   return 0;

 /* If End-Of-Shared-Memory is reached, return 0 bytes written */
 if (smd->offset > smd->size)
   return 0;

 /* How many bytes to write */
 nBytes = nSize * nElem;

 /* Does not exceed End-Of-Shared-Memory */
 if ((smd->offset + nBytes) > smd->size)
   nBytes = (smd->size - smd->offset);

 /* left and right arguments */
 cpLeft  = (char *)(smd->addr)+(smd->offset);
 cpRight = (char *)ptr;

 /* Writes the bytes */
 for(i=0; i<nBytes; i++)
   cpLeft[i] = cpRight[i];

 /* Set smd->offset and return number of elements written */
 smd->offset += nBytes;
 return (nBytes/nSize);
}

/* Close a shared memory [cf: fclose() ] */
int   shm_close(SMEM *smd)
{
 if (smd == NULL)
   return -1;

 shmdt(smd->addr);
 free(smd);
 smd = NULL;
 return 0;
}

/* Delete a shared memory [cf: unlink(file) ] */
int   shm_delete(int key)
{
  int  shmid;
  struct shmid_ds shmid_d;

  /* Try to access shared memory */
  shmid = shmget(key,0,0);
  if (shmid < 0)
    return -1;

  /* Remove the shared memory (equivalent to 'ipcrm -m shmid' ) */
  return shmctl(shmid,IPC_RMID,&shmid_d);
}

/* End of shm_pack.c */
/* Begin of shm_rm.c */

#include <stdio.h>
#include "shm_pack.h"

void main(int argc, char *argv[])
{
  int    i;
  SMEM   *smd;

  if (argc == 1)
  { fprintf(stderr,"Usage: %s <key> ... \n",argv[0]);
    exit(-1);
  }

  for(i=1; i<argc; i++)
  {
    int    key;
    SMEM  *smd;

    key = atoi(argv[i]);
    smd = shm_access(key,0);
    if (smd == NULL)
      fprintf(stderr,"shm_rm: %d non-existent\n",key);
    else
    {
      shm_close(smd);
      shm_delete(key);
    }
  }
}

/* End of shm_rm.c */
/* Begin of shm_touch.c */

#include <stdio.h>
#include <string.h>
#include <time.h>
#include "shm_pack.h"

void main(int argc, char *argv[])
{
  int    i;
  SMEM   *smd;

  if (argc == 1)
  { fprintf(stderr,"Usage: %s <key>[:size] ... \n",argv[0]);
    exit(-1);
  }

  for(i=1; i<argc; i++)
  {
    int    key;
    SMEM  *smd;

    key = atoi(argv[i]);
    smd = shm_access(key,0);
    if (smd != NULL)
      smd->time = time(NULL);
    else
    {
      char *ptr;
      int  size = 1; /* 1-byte shared memory */

      ptr = strchr(argv[i],':');
      if (ptr)
	size = atoi(&ptr[1]);

      smd = shm_create(key,"rw",size); /* create shared memory */
    }

    if (smd == NULL)
      fprintf(stderr,"Cannot create %d\n",key);
  }
}

/* End of shm_touch.c */
/* Begin of shm_pack.h */

/**
 **    shm_pack is a package which manages shared memories like a
 **    memory file. It makes easier the manipulation of shared memories
 **    for any programmer who is familiar with file manipulation.
 **    It provides some protections to avoid to memory corrumption.
 **
 **/

#ifndef SHM_PACK_H_
#define SHM_PACK_H_

#include <time.h>

/*
 * Structure SMEM (Shared memory descriptor). [cf. FILE descriptor]
 *
 */
typedef struct {
 int    key;    /* Shared memory's key */
 void   *addr;  /* Memory address */
 int    size;   /* Size of shared memory */
 int    mode;   /* mode == 0 : cannot write in memory */
 time_t time;   /* time of last modification */
 int    offset; /* current pointer (modified by smSeek() ) */
 } SMEM;


/*
 *  M  A  C  R  O  S
 *
 */

/*
 * Macro: shm_seek(smd,offset)
 * Title: Seek a position into a shared memory [cf: fseek() ]
 *
 * Description:
 *   shm_seek sets the shared memory pointer associated with 'smd' to a
 *   new position that is 'offset' bytes from the current position.
 *   This 'offset' can be negative.
 *
 * Arguments:
 *   smd     : (mod) *SMEM   : pointer on shared memory descriptor.
 *   offset  : (in)  int     : seek 'offset' bytes forward (or backward)
 *
 * Return:
 *   the new position in shared memory (-1 if error)
 *
 */
#define shm_seek(smd,Offset) \
  (NULL == (smd) ? -1 : ((smd)->offset += Offset))

#define shm_rewind(smd) (NULL == (smd) ? -1 : ((smd)->offset = 0))

/*
 * Macro: shm_eosm(smd)
 * Title: Detects end-of-shared memory  [cf: feof() ]
 *
 * Description:
 *   shm_eosm tests if end-of-shared has been sought
 *
 * Arguments:
 *   smd     : (in) *SMEM : pointer on shared memory descriptor.
 *
 * Return:
 *   returns 0 if end-of-shared memory has not been reached,
 *   1 if it has been, and -1 on error.
 *
 */
#define shm_eosm(smd) \
  (smd == NULL ? -1 : ((smd->offset >= smd->size) ? 1 : 0 ))

/*
 *  F  U  N  C  T  I  O  N  S
 *
 */

/*
 * Routine: shm_access(int key, int mode)
 * Title: Access a already-exist shared memory [cf: fopen(file,"r") ]
 *
 * Description:
 *   access a shared memory identified by 'key' and associate a descriptor
 *   with it. Returns the pointer on this descriptor. A shared memory
 *   can be accessed either in read mode or write mode.
 *
 * Arguments:
 *   key     : (in) int : number of the shared memory (unique)
 *   mode    : (in) int : if 0, prevents writing in shared memory
 *
 * Return:
 *   On successful completion, returns a pointer to the newly allocated
 *   descriptor. In the event of error, it return NULL.
 */
SMEM *shm_access(int key, int mode);

/*
 * Routine: shm_create(int key, const char *mode, int size)
 * Title: Create a shared memory [cf: fopen(file,"w") ]
 *
 * Description:
 *   creates a shared memory identified by 'key' and associates a descriptor
 *   with it. Returns the pointer on this descriptor. The size of the
 *   shared memory must be fixed at the creation time.
 *
 * Arguments:
 *   key     : (in) int : number of the shared memory (unique).
 *   size    : (in) int : size of shared memory in bytes.
 *
 * Return:
 *   On successful completion, returns a pointer to the newly allocated
 *   descriptor. In the event of error, it return NULL.
 */
SMEM *shm_create(int key, const char *mode, int size);

/*
 * Routine: shm_delete(int key)
 * Title: Deletes a shared memory [cf: unlink(file) ]
 *
 * Description:
 *   deletes a shared memory identified by 'key'.
 *
 * Arguments:
 *   key     : (in) int : number of the shared memory (unique).
 *
 * Return:
 *   On successful completion, returns 0. On error, returns -1.
 */
int   shm_delete(int key);

/*
 * Routine: shm_read(void *ptr, int nSize, int nElem, SMEM *smd)
 * Title: Reads data from a shared memory [cf: fread() ]
 *
 * Description:
 *   Reads 'nElem' items of data, each of length 'nSize' bytes, from
 *   the given input shared memory descriptor into a block pointed
 *   by ptr.
 *
 * Arguments:
 *   ptr   : (mod) void *   : pointer on a block
 *   nSize : (in)  int      : size of a single item
 *   nElem : (in)  int      : number of items
 *   smd   : (mod) SMEM *   : pointer on shared memory descriptor
 *
 * Return:
 *   On successful completion, returns the number of items (not bytes)
 *   actually read. If this value is less than 'nElem', end-of-shared
 *   memory has been reached.
 */
int   shm_read(void *ptr, int nSize, int nElem, SMEM *smd);

/*
 * Routine: shm_write(void *ptr, int nSize, int nElem, SMEM *smd)
 * Title: Writes data to a shared memory [cf: fwrite() ]
 *
 * Description:
 *   Writes 'nElem' items of data, each of length 'nSize' bytes, from
 *   a block pointed by ptr into the given output shared memory descriptor.
 *
 * Arguments:
 *   ptr   : (mod) void *   : pointer on a block
 *   nSize : (in)  int      : size of a single item
 *   nElem : (in)  int      : number of items
 *   smd   : (mod) SMEM *   : pointer on shared memory descriptor
 *
 * Return:
 *   On successful completion, returns the number of items (not bytes)
 *   actually written. If this value is less than 'nElem', end-of-shared
 *   memory has been reached.
 */
int   shm_write(void *ptr, int nSize, int nElem, SMEM *smd);

/*
 * Routine: shm_close(SMEM *smd)
 * Title: Closes a shared memory [cf: fclose() ]
 *
 * Description:
 *   Self-allocated shared memory descriptor is freed upon closing,
 *   and the shared memory is detached.
 *
 * Arguments:
 *   smd   : (mod) SMEM * : pointer on shared memory descriptor
 *
 * Return:
 *   Returns 0 if successful, and non-zero value on error.
 */
int   shm_close(SMEM *smd);

#endif /* SHM_PACK_H_ */
