mirror of
https://gitlab.freedesktop.org/pulseaudio/pulseaudio.git
synced 2025-10-31 22:25:33 -04:00
commit some work and rename
git-svn-id: file:///home/lennart/svn/public/pulseaudio/trunk@13 fefdeb5f-60dc-0310-8127-8f9354f1896f
This commit is contained in:
parent
0575fc66e9
commit
f78e9b68cb
7 changed files with 268 additions and 28 deletions
|
|
@ -56,3 +56,7 @@ protocol_simple_tcp_la_LIBADD = libprotocol-simple.la libiochannel.la
|
||||||
sink_pipe_la_SOURCES = sink-pipe.c
|
sink_pipe_la_SOURCES = sink-pipe.c
|
||||||
sink_pipe_la_LDFLAGS = -module -avoid-version
|
sink_pipe_la_LDFLAGS = -module -avoid-version
|
||||||
sink_pipe_la_LIBADD = libiochannel.la
|
sink_pipe_la_LIBADD = libiochannel.la
|
||||||
|
|
||||||
|
sink_oss_la_SOURCES = sink-pipe.c
|
||||||
|
sink_oss_la_LDFLAGS = -module -avoid-version
|
||||||
|
sink_oss_la_LIBADD = libiochannel.la
|
||||||
|
|
|
||||||
|
|
@ -27,7 +27,7 @@ struct mainloop_source {
|
||||||
|
|
||||||
struct {
|
struct {
|
||||||
void (*callback)(struct mainloop_source*s, void *userdata);
|
void (*callback)(struct mainloop_source*s, void *userdata);
|
||||||
} prepare;
|
} fixed;
|
||||||
|
|
||||||
struct {
|
struct {
|
||||||
void (*callback)(struct mainloop_source*s, void *userdata);
|
void (*callback)(struct mainloop_source*s, void *userdata);
|
||||||
|
|
@ -47,7 +47,7 @@ struct mainloop_source_list {
|
||||||
};
|
};
|
||||||
|
|
||||||
struct mainloop {
|
struct mainloop {
|
||||||
struct mainloop_source_list io_sources, prepare_sources, idle_sources, signal_sources;
|
struct mainloop_source_list io_sources, fixed_sources, idle_sources, signal_sources;
|
||||||
|
|
||||||
struct pollfd *pollfds;
|
struct pollfd *pollfds;
|
||||||
int max_pollfds, n_pollfds;
|
int max_pollfds, n_pollfds;
|
||||||
|
|
@ -133,7 +133,7 @@ static void free_sources(struct mainloop_source_list *l, int all) {
|
||||||
void mainloop_free(struct mainloop* m) {
|
void mainloop_free(struct mainloop* m) {
|
||||||
assert(m);
|
assert(m);
|
||||||
free_sources(&m->io_sources, 1);
|
free_sources(&m->io_sources, 1);
|
||||||
free_sources(&m->prepare_sources, 1);
|
free_sources(&m->fixed_sources, 1);
|
||||||
free_sources(&m->idle_sources, 1);
|
free_sources(&m->idle_sources, 1);
|
||||||
free_sources(&m->signal_sources, 1);
|
free_sources(&m->signal_sources, 1);
|
||||||
|
|
||||||
|
|
@ -237,14 +237,15 @@ int mainloop_iterate(struct mainloop *m, int block) {
|
||||||
return m->quit;
|
return m->quit;
|
||||||
|
|
||||||
free_sources(&m->io_sources, 0);
|
free_sources(&m->io_sources, 0);
|
||||||
free_sources(&m->prepare_sources, 0);
|
free_sources(&m->fixed_sources, 0);
|
||||||
free_sources(&m->idle_sources, 0);
|
free_sources(&m->idle_sources, 0);
|
||||||
|
free_sources(&m->signal_sources, 0);
|
||||||
|
|
||||||
for (s = m->prepare_sources.sources; s; s = s->next) {
|
for (s = m->fixed_sources.sources; s; s = s->next) {
|
||||||
assert(!s->dead && s->type == MAINLOOP_SOURCE_TYPE_PREPARE);
|
assert(!s->dead && s->type == MAINLOOP_SOURCE_TYPE_FIXED);
|
||||||
if (s->enabled) {
|
if (s->enabled) {
|
||||||
assert(s->prepare.callback);
|
assert(s->fixed.callback);
|
||||||
s->prepare.callback(s, s->userdata);
|
s->fixed.callback(s, s->userdata);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -293,8 +294,8 @@ static struct mainloop_source_list* get_source_list(struct mainloop *m, enum mai
|
||||||
case MAINLOOP_SOURCE_TYPE_IO:
|
case MAINLOOP_SOURCE_TYPE_IO:
|
||||||
l = &m->io_sources;
|
l = &m->io_sources;
|
||||||
break;
|
break;
|
||||||
case MAINLOOP_SOURCE_TYPE_PREPARE:
|
case MAINLOOP_SOURCE_TYPE_FIXED:
|
||||||
l = &m->prepare_sources;
|
l = &m->fixed_sources;
|
||||||
break;
|
break;
|
||||||
case MAINLOOP_SOURCE_TYPE_IDLE:
|
case MAINLOOP_SOURCE_TYPE_IDLE:
|
||||||
l = &m->idle_sources;
|
l = &m->idle_sources;
|
||||||
|
|
@ -351,13 +352,13 @@ struct mainloop_source* mainloop_source_new_io(struct mainloop*m, int fd, enum m
|
||||||
return s;
|
return s;
|
||||||
}
|
}
|
||||||
|
|
||||||
struct mainloop_source* mainloop_source_new_prepare(struct mainloop*m, void (*callback)(struct mainloop_source *s, void*userdata), void*userdata) {
|
struct mainloop_source* mainloop_source_new_fixed(struct mainloop*m, void (*callback)(struct mainloop_source *s, void*userdata), void*userdata) {
|
||||||
struct mainloop_source* s;
|
struct mainloop_source* s;
|
||||||
assert(m && callback);
|
assert(m && callback);
|
||||||
|
|
||||||
s = source_new(m, MAINLOOP_SOURCE_TYPE_PREPARE);
|
s = source_new(m, MAINLOOP_SOURCE_TYPE_FIXED);
|
||||||
|
|
||||||
s->prepare.callback = callback;
|
s->fixed.callback = callback;
|
||||||
s->userdata = userdata;
|
s->userdata = userdata;
|
||||||
s->enabled = 1;
|
s->enabled = 1;
|
||||||
return s;
|
return s;
|
||||||
|
|
|
||||||
|
|
@ -13,7 +13,7 @@ enum mainloop_io_event {
|
||||||
|
|
||||||
enum mainloop_source_type {
|
enum mainloop_source_type {
|
||||||
MAINLOOP_SOURCE_TYPE_IO,
|
MAINLOOP_SOURCE_TYPE_IO,
|
||||||
MAINLOOP_SOURCE_TYPE_PREPARE,
|
MAINLOOP_SOURCE_TYPE_FIXED,
|
||||||
MAINLOOP_SOURCE_TYPE_IDLE,
|
MAINLOOP_SOURCE_TYPE_IDLE,
|
||||||
MAINLOOP_SOURCE_TYPE_SIGNAL
|
MAINLOOP_SOURCE_TYPE_SIGNAL
|
||||||
};
|
};
|
||||||
|
|
@ -26,7 +26,7 @@ int mainloop_run(struct mainloop *m);
|
||||||
void mainloop_quit(struct mainloop *m, int r);
|
void mainloop_quit(struct mainloop *m, int r);
|
||||||
|
|
||||||
struct mainloop_source* mainloop_source_new_io(struct mainloop*m, int fd, enum mainloop_io_event event, void (*callback)(struct mainloop_source*s, int fd, enum mainloop_io_event event, void *userdata), void *userdata);
|
struct mainloop_source* mainloop_source_new_io(struct mainloop*m, int fd, enum mainloop_io_event event, void (*callback)(struct mainloop_source*s, int fd, enum mainloop_io_event event, void *userdata), void *userdata);
|
||||||
struct mainloop_source* mainloop_source_new_prepare(struct mainloop*m, void (*callback)(struct mainloop_source *s, void*userdata), void*userdata);
|
struct mainloop_source* mainloop_source_new_fixed(struct mainloop*m, void (*callback)(struct mainloop_source *s, void*userdata), void*userdata);
|
||||||
struct mainloop_source* mainloop_source_new_idle(struct mainloop*m, void (*callback)(struct mainloop_source *s, void*userdata), void*userdata);
|
struct mainloop_source* mainloop_source_new_idle(struct mainloop*m, void (*callback)(struct mainloop_source *s, void*userdata), void*userdata);
|
||||||
struct mainloop_source* mainloop_source_new_signal(struct mainloop*m, int sig, void (*callback)(struct mainloop_source *s, int sig, void*userdata), void*userdata);
|
struct mainloop_source* mainloop_source_new_signal(struct mainloop*m, int sig, void (*callback)(struct mainloop_source *s, int sig, void*userdata), void*userdata);
|
||||||
|
|
||||||
|
|
|
||||||
234
src/module-oss.c
Normal file
234
src/module-oss.c
Normal file
|
|
@ -0,0 +1,234 @@
|
||||||
|
#include <soundcard.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <sys/stat.h>
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <assert.h>
|
||||||
|
#include <errno.h>
|
||||||
|
#include <string.h>
|
||||||
|
#include <fcntl.h>
|
||||||
|
#include <unistd.h>
|
||||||
|
#include <limits.h>
|
||||||
|
|
||||||
|
#include "iochannel.h"
|
||||||
|
#include "sink.h"
|
||||||
|
#include "source.h"
|
||||||
|
#include "module.h"
|
||||||
|
|
||||||
|
struct userdata {
|
||||||
|
struct sink *sink;
|
||||||
|
struct source *source;
|
||||||
|
struct iochannel *io;
|
||||||
|
struct core *core;
|
||||||
|
|
||||||
|
struct memchunk memchunk, silence;
|
||||||
|
|
||||||
|
uint32_t fragment_size;
|
||||||
|
};
|
||||||
|
|
||||||
|
static void do_write(struct userdata *u) {
|
||||||
|
struct memchunk *memchunk;
|
||||||
|
ssize_t r;
|
||||||
|
assert(u);
|
||||||
|
|
||||||
|
if (!iochannel_is_writable(u->io))
|
||||||
|
return;
|
||||||
|
|
||||||
|
if (!u->memchunk.length) {
|
||||||
|
if (sink_render(u->sink, fragment_size, &u->memchunk) < 0)
|
||||||
|
memchunk = &u->silence;
|
||||||
|
else
|
||||||
|
memchunk = &u->memchunk;
|
||||||
|
}
|
||||||
|
|
||||||
|
assert(memchunk->memblock && memchunk->length);
|
||||||
|
|
||||||
|
if ((r = iochannel_write(u->io, memchunk->memblock->data + memchunk->index, memchunk->length)) < 0) {
|
||||||
|
fprintf(stderr, "write() failed: %s\n", strerror(errno));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (memchunk == &u->silence)
|
||||||
|
assert(r % u->sample_size == 0);
|
||||||
|
else {
|
||||||
|
u->memchunk.index += r;
|
||||||
|
u->memchunk.length -= r;
|
||||||
|
|
||||||
|
if (u->memchunk.length <= 0) {
|
||||||
|
memblock_unref(u->memchunk.memblock);
|
||||||
|
u->memchunk.memblock = NULL;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static void do_read(struct userdata *u) {
|
||||||
|
struct memchunk memchunk;
|
||||||
|
ssize_t r;
|
||||||
|
assert(u);
|
||||||
|
|
||||||
|
if (!iochannel_is_readable(u->io))
|
||||||
|
return;
|
||||||
|
|
||||||
|
memchunk.memblock = memblock_new(u->fragment_size);
|
||||||
|
assert(memchunk.memblock);
|
||||||
|
if ((r = iochannel_read(u->io, memchunk.memblock->data, memchunk->memblock->length)) < 0) {
|
||||||
|
memblock_unref(memchunk.memblock);
|
||||||
|
fprintf(stderr, "read() failed: %s\n", strerror(errno));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
assert(r < memchunk->memblock->length);
|
||||||
|
memchunk.length = memchunk.memblock->length = r;
|
||||||
|
memchunk.index = 0;
|
||||||
|
|
||||||
|
source_post(u->source, &memchunk);
|
||||||
|
memblock_unref(memchunk.memblock);
|
||||||
|
};
|
||||||
|
|
||||||
|
static void io_callback(struct iochannel *io, void*userdata) {
|
||||||
|
struct userdata *u = userdata;
|
||||||
|
assert(u);
|
||||||
|
do_write(u);
|
||||||
|
do_read(u);
|
||||||
|
}
|
||||||
|
|
||||||
|
int module_init(struct core *c, struct module*m) {
|
||||||
|
struct userdata *u = NULL;
|
||||||
|
struct stat st;
|
||||||
|
char *p;
|
||||||
|
int fd = -1;
|
||||||
|
int format, channels, speed, frag_size;
|
||||||
|
int m;
|
||||||
|
const static struct sample_spec ss;
|
||||||
|
assert(c && m);
|
||||||
|
|
||||||
|
p = m->argument ? m->argument : "/dev/dsp";
|
||||||
|
if ((fd = open(p, m = O_RDWR)) >= 0) {
|
||||||
|
int caps;
|
||||||
|
|
||||||
|
ioctl(fd, SNDCTL_DSP_SETDUPLEX, 0);
|
||||||
|
|
||||||
|
if (ioctl(fd, SNDCTL_DSP_GETCAPS, &caps) < 0) {
|
||||||
|
fprintf(stderr, "SNDCTL_DSP_GETCAPS: %s\n", strerror(errno));
|
||||||
|
goto fail;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!(caps & DSP_CAP_DUPLEX)) {
|
||||||
|
close(fd);
|
||||||
|
fd = -1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (fd < 0) {
|
||||||
|
if ((fd = open(p, m = O_WRONLY)) < 0) {
|
||||||
|
if ((fd = open(p, m = O_RDONLY)) < 0) {
|
||||||
|
fprintf(stderr, "open('%s'): %s\n", p, strerror(errno));
|
||||||
|
goto fail;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
frags = 0x7fff0000 | (10 << 16); /* 1024 bytes fragment size */
|
||||||
|
if (ioctl(fd, SNDCTL_DSP_SETFRAGMENT, &frags) < 0) {
|
||||||
|
fprintf(stderr, "SNDCTL_DSP_SETFRAGMENT: %s\n", strerror(errno));
|
||||||
|
goto fail;
|
||||||
|
}
|
||||||
|
|
||||||
|
format = AFMT_S16_NE;
|
||||||
|
if (ioctl(fd, SNDCTL_DSP_SETFMT, &format) < 0 || format != AFMT_S16_NE) {
|
||||||
|
int f = AFMT_S16_NE == AFMT_S16_LE ? AFMT_S16_BE : AFMT_S16_LE;
|
||||||
|
format = f;
|
||||||
|
if (ioctl(fd, SNDCTL_DSP_SETFMT, &format) < 0 || format != f) {
|
||||||
|
format = AFMT_U8;
|
||||||
|
if (ioctl(fd, SNDCTL_DSP_SETFMT, &format) < 0 || format != AFMT_U8) {
|
||||||
|
fprintf(stderr, "SNDCTL_DSP_SETFMT: %s\n", format != AFMT_U8 ? "No supported sample format" : strerror(errno));
|
||||||
|
goto fail;
|
||||||
|
} else
|
||||||
|
ss.format = SAMPLE_U8;
|
||||||
|
} else
|
||||||
|
ss.format = f == AFMT_S16_LE ? SAMPLE_S16LE : SAMPLE_S16BE;
|
||||||
|
} else
|
||||||
|
ss.format = SAMPLE_S16NE;
|
||||||
|
|
||||||
|
channels = 2;
|
||||||
|
if (ioctl(fd, SNDCTL_DSP_CHANNELS, &channels) < 0) {
|
||||||
|
fprintf(stderr, "SNDCTL_DSP_CHANNELS: %s\n", strerror(errno));
|
||||||
|
goto fail;
|
||||||
|
}
|
||||||
|
assert(channels);
|
||||||
|
ss.channels = channels;
|
||||||
|
|
||||||
|
speed = 44100;
|
||||||
|
if (ioctl(fd, SNDCTL_DSP_SPEED, &speed) < 0) {
|
||||||
|
fprintf(stderr, "SNDCTL_DSP_SPEED: %s\n", strerror(errno));
|
||||||
|
goto fail;
|
||||||
|
}
|
||||||
|
assert(speed);
|
||||||
|
ss.rate = speed;
|
||||||
|
|
||||||
|
if (ioctl(fd, SNCTL_DSP_GETBLKSIZE, &frag_size) < 0) {
|
||||||
|
fprintf(stderr, "SNDCTL_DSP_GETBLKSIZE: %s\n", strerror(errno));
|
||||||
|
goto fail;
|
||||||
|
}
|
||||||
|
|
||||||
|
assert(frag_size);
|
||||||
|
|
||||||
|
u = malloc(sizeof(struct userdata));
|
||||||
|
assert(u);
|
||||||
|
|
||||||
|
u->core = c;
|
||||||
|
|
||||||
|
if (m != O_RDONLY) {
|
||||||
|
u->sink = sink_new(c, "dsp", &ss);
|
||||||
|
assert(u->sink);
|
||||||
|
} else
|
||||||
|
u->sink = NULL;
|
||||||
|
|
||||||
|
if (m != O_WRONLY) {
|
||||||
|
u->source = source_new(c, "dsp", &ss);
|
||||||
|
assert(u->source);
|
||||||
|
} else
|
||||||
|
u->source = NULL;
|
||||||
|
|
||||||
|
assert(u->source || u->sink);
|
||||||
|
|
||||||
|
u->io = iochannel_new(c->mainloop, u->source ? fd : -1, u->sink ? fd : 0);
|
||||||
|
assert(u->io);
|
||||||
|
iochannel_set_callback(u->io, io_callback, u);
|
||||||
|
|
||||||
|
u->memchunk.memblock = NULL;
|
||||||
|
u->memchunk.length = 0;
|
||||||
|
|
||||||
|
u->fragment_size = frag_size;
|
||||||
|
u->silence.memblock = memblock_new(u->fragment_size);
|
||||||
|
assert(u->silence);
|
||||||
|
u->silence.length = u->fragment_size;
|
||||||
|
u->silence.index = 0;
|
||||||
|
|
||||||
|
m->userdata = u;
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
fail:
|
||||||
|
if (fd >= 0)
|
||||||
|
close(fd);
|
||||||
|
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
void module_done(struct core *c, struct module*m) {
|
||||||
|
struct userdata *u;
|
||||||
|
assert(c && m);
|
||||||
|
|
||||||
|
u = m->userdata;
|
||||||
|
assert(u);
|
||||||
|
|
||||||
|
if (u->memchunk.memblock)
|
||||||
|
memblock_unref(u->memchunk.memblock);
|
||||||
|
if (u->silence.memblock)
|
||||||
|
memblock_unref(u->silence.memblock);
|
||||||
|
|
||||||
|
sink_free(u->sink);
|
||||||
|
source_free(u->source);
|
||||||
|
iochannel_free(u->io);
|
||||||
|
free(u);
|
||||||
|
}
|
||||||
|
|
@ -91,7 +91,7 @@ struct pstream *pstream_new(struct mainloop *m, struct iochannel *io) {
|
||||||
p->dead = 0;
|
p->dead = 0;
|
||||||
|
|
||||||
p->mainloop = m;
|
p->mainloop = m;
|
||||||
p->mainloop_source = mainloop_source_new_prepare(m, prepare_callback, p);
|
p->mainloop_source = mainloop_source_new_fixed(m, prepare_callback, p);
|
||||||
mainloop_source_enable(p->mainloop_source, 0);
|
mainloop_source_enable(p->mainloop_source, 0);
|
||||||
|
|
||||||
p->send_queue = queue_new();
|
p->send_queue = queue_new();
|
||||||
|
|
|
||||||
|
|
@ -13,6 +13,8 @@
|
||||||
#include "module.h"
|
#include "module.h"
|
||||||
|
|
||||||
struct userdata {
|
struct userdata {
|
||||||
|
char *filename;
|
||||||
|
|
||||||
struct sink *sink;
|
struct sink *sink;
|
||||||
struct iochannel *io;
|
struct iochannel *io;
|
||||||
struct core *core;
|
struct core *core;
|
||||||
|
|
@ -73,7 +75,6 @@ static void io_callback(struct iochannel *io, void*userdata) {
|
||||||
int module_init(struct core *c, struct module*m) {
|
int module_init(struct core *c, struct module*m) {
|
||||||
struct userdata *u = NULL;
|
struct userdata *u = NULL;
|
||||||
struct stat st;
|
struct stat st;
|
||||||
struct sink *sink;
|
|
||||||
char *p;
|
char *p;
|
||||||
int fd = -1;
|
int fd = -1;
|
||||||
const static struct sample_spec ss = {
|
const static struct sample_spec ss = {
|
||||||
|
|
@ -100,17 +101,16 @@ int module_init(struct core *c, struct module*m) {
|
||||||
goto fail;
|
goto fail;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!(sink = sink_new(c, "fifo", &ss))) {
|
|
||||||
fprintf(stderr, "Failed to allocate new sink!\n");
|
|
||||||
goto fail;
|
|
||||||
}
|
|
||||||
|
|
||||||
u = malloc(sizeof(struct userdata));
|
u = malloc(sizeof(struct userdata));
|
||||||
assert(u);
|
assert(u);
|
||||||
|
|
||||||
|
u->filename = strdup(p);
|
||||||
|
assert(u->filename);
|
||||||
u->core = c;
|
u->core = c;
|
||||||
u->sink = sink;
|
u->sink = sink_new(c, "fifo", &ss);
|
||||||
sink_set_notify_callback(sink, notify_callback, u);
|
assert(u->sink);
|
||||||
|
sink_set_notify_callback(u->sink, notify_callback, u);
|
||||||
|
|
||||||
u->io = iochannel_new(c->mainloop, -1, fd);
|
u->io = iochannel_new(c->mainloop, -1, fd);
|
||||||
assert(u->io);
|
assert(u->io);
|
||||||
|
|
@ -119,22 +119,18 @@ int module_init(struct core *c, struct module*m) {
|
||||||
u->memchunk.memblock = NULL;
|
u->memchunk.memblock = NULL;
|
||||||
u->memchunk.length = 0;
|
u->memchunk.length = 0;
|
||||||
|
|
||||||
u->mainloop_source = mainloop_source_new_prepare(c->mainloop, prepare_callback, u);
|
u->mainloop_source = mainloop_source_new_fixed(c->mainloop, prepare_callback, u);
|
||||||
assert(u->mainloop_source);
|
assert(u->mainloop_source);
|
||||||
mainloop_source_enable(u->mainloop_source, 0);
|
mainloop_source_enable(u->mainloop_source, 0);
|
||||||
|
|
||||||
m->userdata = u;
|
m->userdata = u;
|
||||||
|
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
|
|
||||||
fail:
|
fail:
|
||||||
if (fd >= 0)
|
if (fd >= 0)
|
||||||
close(fd);
|
close(fd);
|
||||||
|
|
||||||
if (u)
|
|
||||||
free(u);
|
|
||||||
|
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -151,5 +147,10 @@ void module_done(struct core *c, struct module*m) {
|
||||||
sink_free(u->sink);
|
sink_free(u->sink);
|
||||||
iochannel_free(u->io);
|
iochannel_free(u->io);
|
||||||
mainloop_source_free(u->mainloop_source);
|
mainloop_source_free(u->mainloop_source);
|
||||||
|
|
||||||
|
assert(u->filename);
|
||||||
|
unlink(u->filename);
|
||||||
|
free(u->filename);
|
||||||
|
|
||||||
free(u);
|
free(u);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue