source: abuse/trunk/src/imlib/specs.cpp @ 548

Last change on this file since 548 was 548, checked in by Sam Hocevar, 11 years ago

tool: implement the rename' and del' commands.

File size: 18.9 KB
Line 
1/*
2 *  Abuse - dark 2D side-scrolling platform game
3 *  Copyright (c) 1995 Crack dot Com
4 *  Copyright (c) 2005-2011 Sam Hocevar <sam@hocevar.net>
5 *
6 *  This software was released into the Public Domain. As with most public
7 *  domain software, no warranty is made or implied by Crack dot Com or
8 *  Jonathan Clark.
9 */
10
11#include "config.h"
12
13#include <stdio.h>
14#include <stdlib.h>
15#include <stdint.h>
16#include <ctype.h>
17#include <fcntl.h>
18#include <math.h>
19#include <unistd.h>
20#include <sys/types.h>
21#include <sys/stat.h>
22
23#include "common.h"
24
25#include "image.h"
26#include "palette.h"
27#include "specs.h"
28#include "dprint.h"
29
30char const *spec_types[] =
31{
32    "Invalid type", // 0
33    "Color table",  // 1
34    "Palette",      // 2
35    "Invalid Type", // 3
36    "Image",        // 4
37    "Fore Tile",
38    "Back Tile",
39    "Character",
40    "8 Morph",
41    "16 Morph",
42    "Grue objs",
43    "Extern WAV",
44    "DMX MUS",
45    "Patched morph",
46    "Normal file",
47    "Compress1 file",
48    "Vector Image",
49    "Light list",
50    "Grue fgmap",
51    "Grue bgmap",
52    "Data array",
53    "Character2",
54    "Particle",
55    "Extern lcache"
56};
57
58
59int total_files_open=0;
60char spec_main_file[100];
61
62static char *spec_prefix=NULL;
63static char *save_spec_prefix=NULL;
64
65static jFILE spec_main_jfile((FILE*)0);
66static int spec_main_fd = -1;
67static long spec_main_offset = -1;
68static spec_directory spec_main_sd;
69
70void set_filename_prefix(char const *prefix)
71{
72    if( spec_prefix )
73    {
74        free( spec_prefix );
75    }
76
77    if( prefix )
78    {
79        spec_prefix = strcpy( (char *)malloc( strlen( prefix ) + 2 ), prefix );
80        int len = strlen( prefix );
81        if( prefix[len - 1] != '\\' && prefix[len - 1] != '/')
82        {
83            spec_prefix[len] = '/';
84            spec_prefix[len + 1] = 0;
85        }
86    }
87    else
88    {
89        spec_prefix = NULL;
90    }
91}
92
93char *get_filename_prefix()
94{
95    return spec_prefix;
96}
97
98
99void set_save_filename_prefix(char const *save_prefix)
100{
101    if( save_spec_prefix )
102    {
103        free( save_spec_prefix );
104    }
105
106    if( save_prefix )
107    {
108        int len = strlen( save_prefix );
109        save_spec_prefix = (char *)malloc( len + 1 );
110        strcpy( save_spec_prefix, save_prefix );
111/* AK - Commented this out as it may cause problems
112        if( save_prefix[len - 1] != '\\' && save_prefix[len - 1] != '/' )
113        {
114            save_spec_prefix[len] = '/';
115            save_spec_prefix[len + 1] = '\0';
116        } */
117    }
118    else
119    {
120        save_spec_prefix = NULL;
121    }
122}
123
124char *get_save_filename_prefix()
125{
126  return save_spec_prefix;
127}
128
129int search_order=SPEC_SEARCH_OUTSIDE_INSIDE;
130
131static void (*no_space_handle_fun)()=NULL;
132
133void set_no_space_handler(void (*handle_fun)())
134{
135  no_space_handle_fun=handle_fun;
136}
137
138
139bFILE::bFILE()
140{
141  rbuf_size=8192;
142  rbuf=(unsigned char *)malloc(rbuf_size);
143  rbuf_start=rbuf_end=0;
144
145  wbuf_size=8192;
146  wbuf=(unsigned char *)malloc(wbuf_size);
147  wbuf_end=0;
148}
149
150bFILE::~bFILE()
151{
152  if (rbuf) free(rbuf);
153  flush_writes();
154  if (wbuf) free(wbuf);
155}
156
157int bFILE::flush_writes()
158{
159  if (wbuf_end!=0)
160  {
161    unsigned long ret=unbuffered_write(wbuf,wbuf_end);
162    if (ret!=wbuf_end && no_space_handle_fun)
163      no_space_handle_fun();
164
165    wbuf_end=0;
166    return ret;
167  }
168  return 0;
169}
170
171int bFILE::read(void *buf, size_t count)       // returns number of bytes read, calls unbuffer_read
172{
173  if (!allow_read_buffering())
174    return unbuffered_read(buf,count);
175
176  int total_read=0,error=0;
177  if (!count) return 0;
178  while (count && !error)
179  {
180    if (rbuf_start<rbuf_end)
181    {
182      unsigned int avail_size=rbuf_end-rbuf_start;
183      int copy_size=avail_size>count ? count : avail_size;
184      memcpy(buf,rbuf+rbuf_start,copy_size);
185      buf=(void *)(((unsigned char *)buf)+copy_size);
186      rbuf_start+=copy_size;
187      if (rbuf_start>=rbuf_end)
188      {
189                if (rbuf_end!=rbuf_size)  // buffer wasn't full before so there is no way we can complete read
190                  error=1;
191                rbuf_start=rbuf_end=0;
192      }
193      total_read+=copy_size;
194      count-=copy_size;
195    } else
196    {
197      rbuf_end=unbuffered_read(rbuf,rbuf_size);
198      if (rbuf_end==0) error=1;
199      rbuf_start=0;
200    }
201  }
202  return total_read;
203}
204
205
206int bFILE::write(void const *buf, size_t count)      // returns number of bytes written
207{
208  if (allow_write_buffering())
209  {
210    int total_written=0;
211    while (count)
212    {
213      int copy_size=wbuf_end+count<=wbuf_size ? count :  wbuf_size-wbuf_end;
214      memcpy(wbuf+wbuf_end,buf,copy_size);
215      wbuf_end+=copy_size;
216      count-=copy_size;
217      buf=(void *)(((char *)buf)+copy_size);
218      if (wbuf_end==wbuf_size)
219        if ((unsigned int)flush_writes()!=wbuf_size)
220      return total_written;
221
222      total_written+=copy_size;
223    }
224    return total_written;
225  } else
226  {
227    unsigned long ret=unbuffered_write(buf,count);
228    if (ret!=count && no_space_handle_fun)
229      no_space_handle_fun();
230  }
231  return 0;
232}
233
234int bFILE::seek(long offset, int whence) // whence=SEEK_SET, SEEK_CUR, SEEK_END, ret=0=success
235{
236//    rbuf_start=rbuf_end=0;
237//    unbuffered_seek(offset,SEEK_SET);
238
239  long realpos=unbuffered_tell();
240  long curpos=realpos-rbuf_end+rbuf_start;
241  if (whence==SEEK_CUR) offset+=curpos;
242  else if (whence==SEEK_END) offset=file_size()-offset;
243
244  if (offset<realpos-(long)rbuf_end || offset>=realpos)
245  {
246    rbuf_start=rbuf_end=0;
247    unbuffered_seek(offset,SEEK_SET);
248  } else
249    rbuf_start=rbuf_end-(realpos-offset);
250  return 1;
251}
252
253int bFILE::tell()
254{
255  return unbuffered_tell()-rbuf_end+rbuf_start+
256         wbuf_end;    // if this a write file, add on how much we've written
257}
258
259int bFILE::allow_read_buffering() { return 1; }
260int bFILE::allow_write_buffering() { return 1; }
261
262void set_spec_main_file(char const *filename, int Search_order)
263{
264  dprintf("Specs : main file set to %s\n",filename);
265  strcpy(spec_main_file,filename);
266  search_order=Search_order;
267
268#if (defined(__APPLE__) && !defined(__MACH__))
269  spec_main_jfile.open_external(filename,"rb",O_BINARY|O_RDONLY);
270#else
271  spec_main_jfile.open_external(filename,"rb",O_RDONLY);
272#endif
273  spec_main_fd = spec_main_jfile.get_fd();
274  if (spec_main_fd==-1)
275    return;
276  spec_main_sd.startup(&spec_main_jfile);
277}
278
279jFILE::jFILE(FILE *file_pointer)                       // assumes fp is at begining of file
280{
281  access=0;
282  fd=-1;
283  file_length=0;
284  start_offset=0;
285  flags=JFILE_CLONED;
286}
287
288void jFILE::open_external(char const *filename, char const *mode, int flags)
289{
290  int skip_size=0;
291  char tmp_name[200];
292  if (spec_prefix && filename[0] != '/')
293    sprintf(tmp_name,"%s%s",spec_prefix,filename);
294  else strcpy(tmp_name,filename);
295
296//  int old_mask=umask(S_IRWXU | S_IRWXG | S_IRWXO);
297  if (flags&O_WRONLY)
298  {
299    if ((flags&O_APPEND)==0)
300    {
301      skip_size=1;
302      //int errval = unlink(tmp_name);
303    }
304
305    flags-=O_WRONLY;
306    flags|=O_CREAT|O_RDWR;
307
308    fd=open(tmp_name,flags,S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP | S_IROTH | S_IWOTH);
309  } else
310    fd=open(tmp_name,flags);
311
312//  umask(old_mask);
313  if (fd>=0 && !skip_size)
314  {
315    file_length=lseek(fd,0,SEEK_END);
316    if ((flags&O_APPEND)==0)
317      lseek(fd,0,SEEK_SET);
318    else
319        current_offset = file_length;
320    start_offset=0;
321  } else
322  {
323    file_length=0;
324    start_offset=0;
325  }
326}
327
328
329class null_file : public bFILE     // this file type will use virtual opens inside of a spe
330{
331  public :
332  virtual int open_failure() { return 1; }
333  virtual int unbuffered_read(void *buf, size_t count)   { return 0; }
334  virtual int unbuffered_write(void const *buf, size_t count)  { return 0; }
335  virtual int unbuffered_seek(long offset, int whence)   { return 0; }
336
337  virtual int unbuffered_tell() { return 0; }
338  virtual int file_size() { return 0; }
339  virtual ~null_file() { ; }
340} ;
341
342
343static bFILE *(*open_file_fun)(char const *,char const *)=NULL;
344int (*verify_file_fun)(char const *,char const *)=NULL;
345
346void set_file_opener(bFILE *(*open_fun)(char const *, char const *))
347{
348  open_file_fun=open_fun;
349}
350
351bFILE *open_file(char const *filename, char const *mode)
352{
353  if (!verify_file_fun || verify_file_fun(filename,mode))
354  {
355    if (open_file_fun)
356      return open_file_fun(filename,mode);
357    else return new jFILE(filename,mode);
358  } else return new null_file;
359}
360
361void jFILE::open_internal(char const *filename, char const *mode, int flags)
362{
363  int wr=0;
364  for (char const *s=mode; *s; s++)
365    if (toupper(*s)=='A' || toupper(*s)=='W')
366      wr=1;
367
368  if (wr)
369    fd=-1;                 // only allow extern file openings for writing
370  else
371  {
372       fd = spec_main_fd;
373    if (fd>=0)                    // if we were able to open the main file, see if it's in there
374    {
375      start_offset=0;
376      spec_entry *se=spec_main_sd.find(filename);
377      if (se)
378      {
379    start_offset=se->offset;
380    current_offset = 0;
381    file_length=se->size;
382    rbuf_start=rbuf_end=0;
383      } else
384      {
385    close(fd);
386    fd=-1;
387      }
388    }
389  }
390}
391
392jFILE::jFILE(char const *filename, char const *access_string)      // same as fopen parameters
393{
394 flags=access=0;
395 char const *s=access_string;
396  for (; *s; s++)
397    if (toupper(*s)=='R') access=O_RDONLY;
398
399  for (s=access_string; *s; s++)
400    if (toupper(*s)=='W')
401    {
402      if (access)
403        access=O_RDWR;
404      else access=O_WRONLY;
405    }
406
407  for (s=access_string; *s; s++)
408    if (toupper(*s)=='A')
409      access|=O_APPEND|O_WRONLY;
410
411  file_length=start_offset=-1;
412  current_offset = 0;
413
414  fd=-1;
415  if (search_order==SPEC_SEARCH_OUTSIDE_INSIDE)
416    open_external(filename,access_string,access);
417
418  if (fd<0)
419    open_internal(filename,access_string,access);
420
421  if (fd<0 && search_order==SPEC_SEARCH_INSIDE_OUTSIDE)
422    open_external(filename,access_string,access);
423
424  total_files_open++;
425}
426
427jFILE::~jFILE()
428{
429  flush_writes();
430  if (fd>=0 && !(flags&JFILE_CLONED))
431  {
432    total_files_open--;
433    if (fd != spec_main_fd)
434        close(fd);
435  }
436}
437
438int jFILE::unbuffered_tell()
439{
440//    int ret = ::lseek(fd,0,SEEK_CUR) - start_offset;
441//    if (ret != current_offset)
442//        fprintf(stderr,"Bad tell %d\n",current_offset);
443    return current_offset;
444}
445
446int jFILE::unbuffered_read(void *buf, size_t count)
447{
448    unsigned long len;
449
450    if (fd == spec_main_fd)
451    {
452        if (current_offset+start_offset != spec_main_offset)
453            spec_main_offset = lseek(fd, start_offset+current_offset, SEEK_SET);
454
455        len = ::read(fd,(char*)buf,count);
456        spec_main_offset += len;
457    }
458    else
459    {
460      len = ::read(fd,(char*)buf,count);
461    }
462    current_offset += len;
463    return len;
464}
465
466int jFILE::unbuffered_write(void const *buf, size_t count)
467{
468  long ret = ::write(fd,(char*)buf,count);
469    current_offset += ret;
470    return ret;
471}
472
473int jFILE::unbuffered_seek(long offset, int whence) // whence=SEEK_SET, SEEK_CUR, SEEK_END, ret=0=success
474{
475  long ret;
476
477  switch (whence)
478  {
479    case SEEK_SET :
480    { ret = lseek(fd,start_offset+offset,SEEK_SET); } break;
481    case SEEK_END :
482    { ret = lseek(fd,start_offset+file_length-offset,SEEK_SET); } break;
483    case SEEK_CUR :
484    { ret = lseek(fd,offset,SEEK_CUR); } break;
485    default:
486        ret = -1;
487        break;
488  }
489  if (ret>=0)
490  {
491    current_offset = ret - start_offset;
492    if (spec_main_fd == fd)
493      spec_main_offset = ret;
494    return ret;
495  }
496  else
497    return -1;  // if a bad whence, then failure
498}
499
500
501uint8_t bFILE::read_uint8()
502{ uint8_t x;
503  read(&x,1);
504  return x;
505}
506
507uint16_t bFILE::read_uint16()
508{
509  uint16_t x;
510  read(&x,2);
511  return lstl(x);
512}
513
514
515uint32_t bFILE::read_uint32()
516{
517  uint32_t x;
518  read(&x,4);
519  return lltl(x);
520}
521
522void bFILE::write_uint8(uint8_t x)
523{
524  write(&x,1);
525}
526
527void bFILE::write_uint16(uint16_t x)
528{
529  x=lstl(x);
530  write(&x,2);
531}
532
533
534void bFILE::write_uint32(uint32_t x)
535{
536  x=lltl(x);
537  write(&x,4);
538}
539
540void bFILE::write_double(double x)
541{
542  double a;
543  write_uint32((long)(modf(x,&a)*(double)(1<<31)));
544  write_uint32((long)a);
545}
546
547double bFILE::read_double()
548{
549  long a,b;
550  a=read_uint32();
551  b=read_uint32();
552  return (double)b+a/(double)(1<<31);
553}
554
555spec_directory::~spec_directory()
556{
557
558  if (total)
559  {
560    free(data);
561    free(entries);
562  }
563}
564
565void spec_directory::FullyLoad(bFILE *fp)
566{
567    for (int i = 0; i < total; i++)
568    {
569        spec_entry *se = entries[i];
570        free(se->data);
571        se->data = malloc(se->size);
572        fp->seek(se->offset, SEEK_SET);
573        fp->read(se->data, se->size);
574    }
575}
576
577spec_entry::spec_entry(uint8_t spec_type, char const *object_name,
578                       char const *link_filename,
579                       unsigned long data_size, unsigned long data_offset)
580{
581    type = spec_type;
582    name = strdup(object_name);
583    data = NULL;
584    size = data_size;
585    offset = data_offset;
586}
587
588spec_entry::~spec_entry()
589{
590    free(name);
591    free(data);
592}
593
594void spec_entry::Print()
595{
596    printf("%15s%25s%8ld%8ld\n", spec_types[type], name, size, offset);
597}
598
599void spec_directory::calc_offsets()
600{
601    size_t o = SPEC_SIG_SIZE + 2;
602
603    // calculate the size of directory info
604    for (int i = 0; i < total; i++)
605        o += 1 + 1 + strlen(entries[i]->name) + 1 + 1 + 8;
606
607    // calculate offset for each entry
608    for (int i = 0; i < total; i++)
609    {
610        entries[i]->offset = o;
611        o += entries[i]->size;
612    }
613}
614
615spec_entry *spec_directory::find(char const *name, int type)
616{
617  int i;
618  spec_entry **e;
619  for (i=0,e=entries; i<total; i++,e++)
620    if (!strcmp((*e)->name,name) && (*e)->type==type)
621      return (*e);
622  return NULL;
623}
624
625spec_entry *spec_directory::find(char const *name)
626{
627  int i;
628  spec_entry **e;
629  for (i=0,e=entries; i<total; i++,e++)
630    if (!strcmp((*e)->name,name))
631      return (*e);
632  return NULL;
633}
634
635long spec_directory::find_number(char const *name)
636{
637  int i;
638  spec_entry **e;
639  for (i=0,e=entries; i<total; i++,e++)
640    if (!strcmp((*e)->name,name))
641      return i;
642  return -1;
643}
644
645spec_entry *spec_directory::find(int type)
646{
647  int i;
648  spec_entry **e;
649  for (i=0,e=entries; i<total; i++,e++)
650    if ((*e)->type==type)
651      return (*e);
652  return NULL;
653}
654
655long spec_directory::type_total(int type)
656{
657  int i,x=0;
658  spec_entry **e;
659  for (i=0,e=entries; i<total; i++,e++)
660    if ((*e)->type==type) x++;
661  return x;
662}
663
664long spec_directory::find_number(int type)
665{
666  int i;
667  spec_entry **e;
668  for (i=0,e=entries; i<total; i++,e++)
669    if ((*e)->type==type)
670      return i;
671  return -1;
672}
673
674void spec_directory::print()
675{
676  spec_entry **se;
677  int i;
678  printf("[   Entry type   ][   Entry name   ][  Size  ][ Offset ]\n");
679  for (i=0,se=entries; i<total; i++,se++)
680    (*se)->Print();
681}
682
683
684void spec_directory::startup(bFILE *fp)
685{
686  char buf[256];
687  memset(buf,0,256);
688  fp->read(buf,8);
689  buf[9]=0;
690  size=0;
691  if (!strcmp(buf,SPEC_SIGNATURE))
692  {
693    total=fp->read_uint16();
694    entries=(spec_entry **)malloc(sizeof(spec_entry *)*total);
695    long start=fp->tell();
696
697    int i;
698    for (i=0; i<total; i++)
699    {
700      fp->read(buf,2);
701      long entry_size=sizeof(spec_entry)+(unsigned char)buf[1];
702      entry_size=(entry_size+3)&(~3);
703      fp->read(buf,(unsigned char)buf[1]);
704      fp->read(buf,9);
705
706      size+=entry_size;
707    }
708    data=malloc(size);
709    char *dp=(char *)data;
710    fp->seek(start,SEEK_SET);
711    for (i=0; i<total; i++)
712    {
713      spec_entry *se=(spec_entry *)dp;
714      entries[i]=se;
715
716      unsigned char len,flags,type;
717      fp->read(&type,1);
718      fp->read(&len,1);
719      se->type=type;
720      se->data = NULL;
721      se->name=dp+sizeof(spec_entry);
722      fp->read(se->name,len);
723      fp->read(&flags,1);
724
725      se->size=fp->read_uint32();
726      se->offset=fp->read_uint32();
727      dp+=((sizeof(spec_entry)+len)+3)&(~3);
728    }
729  }
730  else
731  {
732    total=0;
733    data=NULL;
734    entries=NULL;
735  }
736}
737
738
739spec_directory::spec_directory(bFILE *fp)
740{ startup(fp); }
741
742spec_directory::spec_directory(FILE *fp)
743{
744  jFILE jfp(fp);
745  startup(&jfp);
746}
747
748spec_directory::spec_directory()
749{
750  size=0;
751  total=0;
752  data=NULL;
753  entries=NULL;
754}
755
756/*
757spec_directory::spec_directory(char *filename)
758{
759  jFILE *fp;
760  if (filename)
761  {
762    fp=new jFILE(filename,"rb");
763    if (!fp->open_failure())
764      startup(fp);
765    else
766    {
767      total=0;
768      entries=NULL;
769    }
770    delete fp;
771  } else printf("NULL filename to spec_directory::spec_directory\n");
772}*/
773
774int write_string(bFILE *fp, char const *st)
775{
776  unsigned char length=strlen(st)+1;
777  if (fp->write(&length,1)!=1) return 0;
778  if (fp->write(st,length)!=length) return 0;
779  return 1;
780}
781
782long spec_directory::data_start_offset()
783{
784    /* FIXME: no need for a for loop here! */
785    long i;
786    for(i = 0; i < total; i++)
787        return entries[i]->offset;
788
789    // If no entries, then no data, but return where it would start anyway
790    return SPEC_SIG_SIZE + 2;
791}
792
793long spec_directory::data_end_offset()
794{
795    /* FIXME: no need for a for loop here! */
796  spec_entry **e;
797  long i;
798  for (i=total-1,e=entries; i>=0; i--,e++)
799    return (*e)->offset+(*e)->size;
800
801  return SPEC_SIG_SIZE+2;
802}
803
804int spec_directory::write(bFILE *fp)
805{
806
807  char sig[SPEC_SIG_SIZE];
808  unsigned char flags=0;
809  unsigned long offset,data_size;
810  spec_entry **e;
811  strcpy(sig,SPEC_SIGNATURE);
812
813  if (fp->write(sig,sizeof(sig))!=sizeof(sig))    return 0;
814  fp->write_uint16(total);
815
816
817  int i;
818  for (i=0,e=entries; i<total; i++,e++)
819  {
820    if (fp->write(&(*e)->type,1)!=1)                 return 0;
821    if (!write_string(fp,(*e)->name))                return 0;
822    flags=0;
823    if (fp->write(&flags,1)!=1)                     return 0;
824
825    data_size=lltl((*e)->size);
826    if (fp->write((char *)&data_size,4)!=4)              return 0;
827    offset=lltl((*e)->offset);
828    if (fp->write((char *)&offset,4)!=4)                  return 0;
829
830  }
831  return 1;
832}
833
834jFILE *spec_directory::write(char const *filename)
835{
836  jFILE *fp;
837  fp=new jFILE(filename,"wb");
838  if (fp->open_failure()) { delete fp; return NULL; }
839  if (!write(fp))
840  {
841    delete fp;
842    return NULL;
843  } else return fp;
844
845}
846
847uint16_t read_uint16(FILE *fp)
848{
849  uint16_t x;
850  fread(&x,1,2,fp);
851  return lstl(x);
852}
853
854uint32_t read_uint32(FILE *fp)
855{
856  uint32_t x;
857  fread(&x,1,4,fp);
858  return lltl(x);
859}
860void write_uint16(FILE *fp, uint16_t x)
861{
862  x=lstl(x);
863  fwrite(&x,1,2,fp);
864}
865
866void write_uint32(FILE *fp, uint32_t x)
867{
868  x=lltl(x);
869  fwrite(&x,1,4,fp);
870}
871
872uint8_t read_uint8(FILE *fp) { return fgetc(fp)&0xff; }
873void write_uint8(FILE *fp, uint8_t x) { fputc((unsigned char)x,fp); }
874
875void spec_directory::remove(spec_entry *e)
876{
877  int i;
878  for (i=0; i<total && entries[i]!=e; i++);            // find the entry in the array first
879
880  if (entries[i]==e)                                 // make sre it was found
881  {
882    delete e;
883    total--;
884    for (; i<total; i++)                               // compact the pointer array
885      entries[i]=entries[i+1];
886    entries=(spec_entry **)realloc(entries,sizeof(spec_entry *)*total);
887  }
888  else
889    printf("Spec_directory::remove bad entry pointer\n");
890}
891
892
893
894void spec_directory::add_by_hand(spec_entry *e)
895{
896  total++;
897  entries=(spec_entry **)realloc(entries,sizeof(spec_entry *)*total);
898  entries[total-1]=e;
899}
900
901void spec_directory::delete_entries()   // if the directory was created by hand instead of by file
902{
903  int i;
904  for (i=0; i<total; i++)
905    delete entries[i];
906
907  if (total)
908    free(entries);
909}
910
911void note_open_fd(int fd, char const *str)
912{
913    total_files_open++;
914}
915
916void note_close_fd(int fd)
917{
918    total_files_open--;
919}
920
921void list_open_fds()
922{
923    printf("Total open file descriptors: %d\n", total_files_open);
924}
925
Note: See TracBrowser for help on using the repository browser.