/*
* options.c
*
* Copyright (C) 1997,98 Rasca, Berlin
* Copyright (C) 2003-06 Karl H. Beckers, Frankfurt
* EMail: khb@jarre-de-the.net
*
* 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.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*
*/
#ifdef HAVE_CONFIG_H
# include <config.h>
#endif
#include <stdio.h>
#include <stdlib.h>
#include <limits.h>
#include <ctype.h>
#include "job.h"
#include "app_data.h"
#include "codecs.h"
#include "xvidcap-intl.h"
#define OPS_FILE ".xvidcaprc"
extern AppData *app;
extern xvCodec tCodecs[NUMCODECS];
extern xvFFormat tFFormats[NUMCAPS];
extern xvAuCodec tAuCodecs[NUMAUCODECS];
/*
* save the values
*/
Boolean xvc_write_options_file()
{
int n;
char *home, *element = NULL;
char file[PATH_MAX + 1];
FILE *fp;
Job *job = xvc_job_ptr();
// save it to $HOME/.xvidcaprc
home = getenv("HOME");
sprintf(file, "%s/%s", home, OPS_FILE);
fp = fopen(file, "w");
if (!fp) {
perror(file);
return FALSE;
}
fprintf(fp, _("#\n# xvidcap configuration file\n\n"));
// general options
fprintf(fp, _("#general options ...\n"));
fprintf(fp,
_
("# default capture mode (0 = single-frame, 1 = multi-frame\ndefault_mode: %i\n"),
#ifdef USE_FFMPEG
app->default_mode);
#else // USE_FFMPEG
0);
#endif // USE_FFMPEG
fprintf(fp, _("# capture source\nsource: %s\n"), app->source);
fprintf(fp, _("# hide GUI\nnogui: %d\n"),
((app->flags & FLG_NOGUI) ? 1 : 0));
#ifdef HAVE_FFMPEG_AUDIO
fprintf(fp, _("# device to grab audio from\naudio_in: %s\n"),
((strcmp(app->snddev, "pipe:") == 0) ? "-" : app->snddev));
#endif // HAVE_FFMPEG_AUDIO
fprintf(fp,
_
("# what kind of mouse pointer should be recorded? 0 = none, 1 = white, 2 = black\n"));
fprintf(fp, "mouse_wanted: %i\n", app->mouseWanted);
fprintf(fp,
_
("# should cap_geometry be saved to prefs?\nsave_geometry: %d\n"),
((app->flags & FLG_SAVE_GEOMETRY) ? 1 : 0));
if ((app->flags & FLG_SAVE_GEOMETRY) > 0) {
fprintf(fp, _("# area to capture as in --capture_geometry\n"));
fprintf(fp, "cap_geometry: %ix%i", job->area->width,
job->area->height);
if (job->area->x >= 0 && job->area->y >= 0)
fprintf(fp, "+%i+%i\n", job->area->x, job->area->y);
else
fprintf(fp, "\n");
}
fprintf(fp, _("# show time rather than frame count\nshow_time: %d\n"),
((app->flags & FLG_SHOW_TIME) ? 1 : 0));
fprintf(fp, _("# toggle autocontinue (0/1) \nauto_continue: %i\n"),
((app->flags & FLG_AUTO_CONTINUE) ? 1 : 0));
fprintf(fp,
_
("# always show results dialog (0/1) \nalways_show_results: %i\n"),
((app->flags & FLG_ALWAYS_SHOW_RESULTS) ? 1 : 0));
fprintf(fp,
_
("# rescale the captured area to n percent of the original\n"));
fprintf(fp, "rescale: %i\n", (app->rescale));
fprintf(fp, _("\n#options for single-frame capture ...\n"));
fprintf(fp,
_
("# file pattern\n# this defines the filetype to write via the extension provided\n"));
fprintf(fp, _("# valid extensions are: "));
#ifdef USE_FFMPEG
for (n = CAP_NONE; n < CAP_FFM; n++) {
#else
for (n = CAP_NONE; n < NUMCAPS; n++) {
#endif // USE_FFMPEG
if (tFFormats[n].extensions) {
element = xvc_next_element(tFFormats[n].extensions);
while (element != NULL) {
fprintf(fp, "%s", element);
element = xvc_next_element(NULL);
if (element != NULL)
fprintf(fp, ", ");
}
#ifdef USE_FFMPEG
if (n < (CAP_FFM - 1))
fprintf(fp, ", ");
#else
if (n < (NUMCAPS - 1))
fprintf(fp, ", ");
#endif // USE_FFMPEG
}
}
fprintf(fp, "\n");
fprintf(fp, "sf_file: %s\n", app->single_frame.file);
fprintf(fp,
_
("# file format - use AUTO to select format through file extension\n"));
fprintf(fp, _("# Otherwise specify one of the following: "));
#ifdef USE_FFMPEG
for (n = (CAP_NONE + 1); n < CAP_MF; n++) {
#else
for (n = (CAP_NONE + 1); n < NUMCAPS; n++) {
#endif // USE_FFMPEG
if (tFFormats[n].name) {
element = xvc_next_element(tFFormats[n].name);
while (element != NULL) {
fprintf(fp, "%s", element);
element = xvc_next_element(NULL);
if (element != NULL)
fprintf(fp, ", ");
}
if (n < (NUMCAPS - 1))
fprintf(fp, ", ");
}
}
fprintf(fp, "\n");
fprintf(fp, "sf_format: %s\n",
tFFormats[app->single_frame.target].name);
fprintf(fp,
_
("# video codec used by ffmpeg - use AUTO to auto-detect codec\n"));
fprintf(fp, _("# Otherwise specify one of the following: "));
#ifdef USE_FFMPEG
for (n = (CODEC_NONE + 1); n < CODEC_MF; n++) {
#else
for (n = (CODEC_NONE + 1); n < NUMCODECS; n++) {
#endif // USE_FFMPEG
if (tCodecs[n].name) {
element = xvc_next_element(tCodecs[n].name);
while (element != NULL) {
fprintf(fp, "%s", element);
element = xvc_next_element(NULL);
if (element != NULL)
fprintf(fp, ", ");
}
if (n < (NUMCODECS - 1))
fprintf(fp, ", ");
}
}
fprintf(fp, "\n#sf_codec: %s\n",
tCodecs[app->single_frame.targetCodec].name);
fprintf(fp, _("# audio codec (for future use)\nsf_au_codec: %s\n"),
"NONE");
fprintf(fp, _("# frames per second\nsf_fps: %.2f\n"),
((float) app->single_frame.fps / 100));
fprintf(fp, _("# max time (0 = unlimited)\nsf_max_time: %i\n"),
app->single_frame.time);
fprintf(fp, _("# max frames (0 = unlimited)\nsf_max_frames: %d\n"),
app->single_frame.frames);
fprintf(fp, _("# number to start counting individual frames from\n"));
fprintf(fp, "sf_start_no: %i\n", app->single_frame.start_no);
fprintf(fp, _("# quality (JPEG/MPEG)\nsf_quality: %d\n"),
app->single_frame.quality);
fprintf(fp,
_
("# toggle audio capture (0/1) (for future use)\nsf_audio: 0\n"));
fprintf(fp,
_
("# sample rate for audio capture (for future use)\nsf_audio_rate: 0\n"));
fprintf(fp,
_
("# bit rate for audio capture (for future use)\nsf_audio_bits: 0\n"));
fprintf(fp,
_
("# number of channels to use in audio capture (for future use)\n"));
fprintf(fp, "sf_audio_channels: 0\n");
fprintf(fp, _("# command to display captured frames as animation\n"));
fprintf(fp, "sf_animate_cmd: %s\n", app->single_frame.play_cmd);
fprintf(fp, _("# command to edit current frame\n"));
fprintf(fp, "sf_edit_cmd: %s\n", app->single_frame.edit_cmd);
fprintf(fp, _("# command to encode captured frames\n"));
fprintf(fp, "sf_video_cmd: %s\n", app->single_frame.video_cmd);
#ifdef USE_FFMPEG
fprintf(fp, _("\n#options for multi-frame capture ...\n"));
fprintf(fp,
_
("# file pattern\n# this defines the filetype to write via the extension provided\n"));
fprintf(fp, _("# valid extensions are: "));
for (n = CAP_FFM; n < NUMCAPS; n++) {
if (tFFormats[n].extensions) {
element = xvc_next_element(tFFormats[n].extensions);
while (element != NULL) {
fprintf(fp, ".%s", element);
element = xvc_next_element(NULL);
if (element != NULL)
fprintf(fp, ", ");
}
}
}
fprintf(fp, "\n");
fprintf(fp, "mf_file: %s\n", app->multi_frame.file);
fprintf(fp,
_
("# file format - use AUTO to select format through file extension\n"));
fprintf(fp, _("# Otherwise specify one of the following: "));
for (n = CAP_MF; n < NUMCAPS; n++) {
if (tFFormats[n].name) {
element = xvc_next_element(tFFormats[n].name);
while (element != NULL) {
fprintf(fp, "%s", element);
element = xvc_next_element(NULL);
if (element != NULL)
fprintf(fp, ", ");
}
if (n < (NUMCAPS - 1))
fprintf(fp, ", ");
}
}
fprintf(fp, "\n");
fprintf(fp, "mf_format: %s\n",
tFFormats[app->multi_frame.target].name);
fprintf(fp,
_
("# video codec used by ffmpeg - use AUTO to auto-detect codec\n"));
fprintf(fp, _("# Otherwise specify one of the following: "));
for (n = CODEC_MF; n < NUMCODECS; n++) {
if (tCodecs[n].name) {
element = xvc_next_element(tCodecs[n].name);
while (element != NULL) {
fprintf(fp, "%s", element);
element = xvc_next_element(NULL);
if (element != NULL)
fprintf(fp, ", ");
}
if (n < (NUMCODECS - 1))
fprintf(fp, ", ");
}
}
fprintf(fp, "\nmf_codec: %s\n",
tCodecs[app->multi_frame.targetCodec].name);
fprintf(fp,
_
("# audio codec used by ffmpeg - use AUTO to auto-detect audio codec\n"));
fprintf(fp, _("# Otherwise specify one of the following: "));
for (n = (AU_CODEC_NONE + 1); n < NUMAUCODECS; n++) {
if (tAuCodecs[n].name) {
element = xvc_next_element(tAuCodecs[n].name);
while (element != NULL) {
fprintf(fp, "%s", element);
element = xvc_next_element(NULL);
if (element != NULL)
fprintf(fp, ", ");
}
if (n < (NUMAUCODECS - 1))
fprintf(fp, ", ");
}
}
#ifdef HAVE_FFMPEG_AUDIO
fprintf(fp, "\nmf_au_codec: %s\n",
tAuCodecs[app->multi_frame.au_targetCodec].name);
#endif // HAVE_FFMPEG_AUDIO
fprintf(fp, _("# frames per second\nmf_fps: %.2f\n"),
((float) app->multi_frame.fps / 100));
fprintf(fp, _("# max time (0 = unlimited)\nmf_max_time: %i\n"),
app->multi_frame.time);
fprintf(fp, _("# max frames (0 = unlimited)\nmf_max_frames: %d\n"),
app->multi_frame.frames);
fprintf(fp,
_
("# number to start counting individual frames from (for future use)\n"));
fprintf(fp, "mf_start_no: %i\n", app->multi_frame.start_no);
fprintf(fp, _("# quality (JPEG/MPEG)\nmf_quality: %d\n"),
app->multi_frame.quality);
#ifdef HAVE_FFMPEG_AUDIO
fprintf(fp, _("# toggle audio capture (0/1)\nmf_audio: %i\n"),
((app->multi_frame.audioWanted == 1) ? 1 : 0));
fprintf(fp, _("# sample rate for audio capture\nmf_audio_rate: %i\n"),
app->multi_frame.sndrate);
fprintf(fp, _("# bit rate for audio capture\nmf_audio_bits: %i\n"),
app->multi_frame.sndsize);
fprintf(fp, _("# number of channels to use in audio capture\n"));
fprintf(fp, "mf_audio_channels: %i\n", app->multi_frame.sndchannels);
#endif // HAVE_FFMPEG_AUDIO
fprintf(fp, _("# command to display captured frames as animation\n"));
fprintf(fp, "mf_animate_cmd: %s\n", app->multi_frame.play_cmd);
fprintf(fp, _("# command to edit current movie\n"));
fprintf(fp, "mf_edit_cmd: %s\n", app->multi_frame.edit_cmd);
fprintf(fp,
_
("# command to encode captured frames (subject to change)\n"));
fprintf(fp, "mf_video_cmd: %s\n", app->multi_frame.video_cmd);
#endif // USE_FFMPEG
fclose(fp);
return TRUE;
}
/*
* read options file
*/
Boolean xvc_read_options_file()
{
char line[260];
char *home;
char file[PATH_MAX + 1];
FILE *fp;
home = getenv("HOME");
sprintf(file, "%s/%s", home, OPS_FILE);
fp = fopen(file, "r");
if (fp != NULL) {
while (fgets(line, 255, fp)) {
char *token, *value;
char *n_hash;
char *c_hash = strchr(line, '#');
// ignore comments
if (c_hash != NULL)
sprintf(c_hash, "\0");
// get rid of newlines
n_hash = strchr(line, '\n');
if (n_hash != NULL)
sprintf(n_hash, "\0");
// if smth. is left, parse line
if (strlen(line) > 0) {
char low_token[260];
int y;
token = strtok(line, " :=\"");
// this has found the first token
// for the value we need special treatment for the command
//
//
// parameters which
// consist of multiple words
// all other values are just a single token
for (y = 0; y < strlen(token); y++) {
low_token[y] = tolower(token[y]);
}
low_token[strlen(token)] = '\0';
if (strstr(low_token, "_animate_cmd") != NULL
|| strstr(low_token, "_edit_cmd") != NULL
|| strstr(low_token, "_video_cmd") != NULL
|| strcasecmp(token, "help_cmd") == 0) {
int x = 1;
while (line[strlen(token) + x] == ' '
|| line[strlen(token) + x] == ':'
|| line[strlen(token) + x] == '=')
x++;
value = strdup(&line[strlen(token) + x]);
} else {
value = strtok(NULL, " :=\"");
if (!value)
value = "";
}
// general options first ...
#ifdef USE_FFMPEG
if (strcasecmp(token, "default_mode") == 0) {
app->default_mode = atoi(value);
} else
#else // USE_FFMPEG
app->default_mode = 0;
#endif // USE_FFMPEG
if (strcasecmp(token, "source") == 0) {
app->source = strdup(value);
} else if (strcasecmp(token, "nogui") == 0) {
if (atoi(value) == 1)
app->flags |= FLG_NOGUI;
else if (atoi(value) == 0)
app->flags &= ~FLG_NOGUI;
else {
app->flags &= ~FLG_NOGUI;
fprintf(stderr,
_
("reading unsupported GUI preferences from options file\nresetting to GUI not hidden\n"));
}
}
#ifdef HAVE_FFMPEG_AUDIO
else if (strcasecmp(token, "audio_in") == 0) {
app->snddev = strdup(value);
}
#endif // HAVE_FFMPEG_AUDIO
else if (strcasecmp(token, "mouse_wanted") == 0) {
app->mouseWanted = atoi(value);
} else if (strcasecmp(token, "save_geometry") == 0) {
if (atoi(value) == 1)
app->flags |= FLG_SAVE_GEOMETRY;
else if (atoi(value) == 0)
app->flags &= ~FLG_SAVE_GEOMETRY;
else {
app->flags &= ~FLG_SAVE_GEOMETRY;
fprintf(stderr,
_
("reading unsupported save_geometry preferences from options file\nresetting to capture geometry not saved\n"));
}
} else if (strcasecmp(token, "cap_geometry") == 0) {
sscanf(value, "%dx%d+%d+%d", &(app->cap_width),
&(app->cap_height), &(app->cap_pos_x),
&(app->cap_pos_y));
} else if (strcasecmp(token, "show_time") == 0) {
// FIXME: this is not at all implemented, yet
if (atoi(value) == 1)
app->flags |= FLG_SHOW_TIME;
else if (atoi(value) == 0)
app->flags &= ~FLG_SHOW_TIME;
else {
app->flags &= ~FLG_SHOW_TIME;
fprintf(stderr,
_
("reading unsupported show_time value from options file\nresetting to display frame counter\n"));
}
} else if (strcasecmp(token, "auto_continue") == 0) {
if (atoi(value) == 1)
app->flags |= FLG_AUTO_CONTINUE;
else if (atoi(value) == 0)
app->flags &= ~FLG_AUTO_CONTINUE;
else {
app->flags |= FLG_AUTO_CONTINUE;
fprintf(stderr,
_
("reading unsupported auto_continue value from options file\nresetting to autocontinue enabled.\n"));
}
} else if (strcasecmp(token, "always_show_results") == 0) {
if (atoi(value) == 1)
app->flags |= FLG_ALWAYS_SHOW_RESULTS;
else if (atoi(value) == 0)
app->flags &= ~FLG_ALWAYS_SHOW_RESULTS;
else {
app->flags |= FLG_ALWAYS_SHOW_RESULTS;
fprintf(stderr,
_
("reading unsupported always_show_results value from options file\nresetting to always show results.\n"));
}
} else if (strcasecmp(token, "rescale") == 0) {
if (value)
app->rescale = atoi(value);
// now single-frame capture options
} else if (strcasecmp(token, "sf_file") == 0) {
app->single_frame.file = strdup(value);
} else if (strcasecmp(token, "sf_format") == 0) {
int cap_index = 0, found_target = 0;
for (cap_index = CAP_NONE; cap_index < NUMCAPS;
cap_index++) {
if (strcasecmp(tFFormats[cap_index].name, token) ==
0)
found_target = cap_index;
}
app->single_frame.target = found_target;
} else if (strcasecmp(token, "sf_codec") == 0) {
int codec_index = 0, found_codec = 0;
for (codec_index = CODEC_NONE; codec_index < NUMCODECS;
codec_index++) {
if (strcasecmp(tCodecs[codec_index].name, token) ==
0)
found_codec = codec_index;
}
app->single_frame.targetCodec = found_codec;
}
#ifdef HAVE_FFMPEG_AUDIO
else if (strcasecmp(token, "sf_au_codec") == 0) {
int auCodec_index = 0, found_auCodec = 0;
for (auCodec_index = AU_CODEC_NONE;
auCodec_index < NUMAUCODECS; auCodec_index++) {
if (strcasecmp
(tAuCodecs[auCodec_index].name, token) == 0)
found_auCodec = auCodec_index;
}
app->single_frame.au_targetCodec = found_auCodec;
}
#endif // HAVE_FFMPEG_AUDIO
else if (strcasecmp(token, "sf_fps") == 0) {
app->single_frame.fps =
xvc_get_int_from_float_string(value);
} else if (strcasecmp(token, "sf_max_time") == 0) {
app->single_frame.time = atoi(value);
if (atof(value) > 0)
app->single_frame.frames = 0;
} else if (strcasecmp(token, "sf_max_frames") == 0) {
app->single_frame.frames = atoi(value);
if (atoi(value) > 0)
app->single_frame.time = 0;
} else if (strcasecmp(token, "sf_start_no") == 0) {
app->single_frame.start_no = atof(value);
} else if (strcasecmp(token, "sf_quality") == 0) {
app->single_frame.quality = atoi(value);
if ((app->single_frame.quality < 0)
|| (app->single_frame.quality > 100))
app->single_frame.quality = 75; /* reset to
* default */
} else if (strcasecmp(token, "sf_audio_rate") == 0) {
// for future use
} else if (strcasecmp(token, "sf_ audio_bits") == 0) {
// for future use
} else if (strcasecmp(token, "sf_audio_channels") == 0) {
// for future use
} else if (strcasecmp(token, "sf_animate_cmd") == 0) {
app->single_frame.play_cmd = strdup(value);
} else if (strcasecmp(token, "sf_edit_cmd") == 0) {
app->single_frame.edit_cmd = strdup(value);
} else if (strcasecmp(token, "sf_video_cmd") == 0) {
app->single_frame.video_cmd = strdup(value);
}
#ifdef USE_FFMPEG
// now multi-frame capture options
else if (strcasecmp(token, "mf_file") == 0) {
app->multi_frame.file = strdup(value);
} else if (strcasecmp(token, "mf_format") == 0) {
int n, a = -1;
for (n = CAP_FFM; n < NUMCAPS; n++) {
if (strcasecmp(value, tFFormats[n].name) == 0)
a = n;
}
if (strcasecmp(value, "NONE") == 0
|| strcasecmp(value, "AUTO") == 0) {
// flag set target by filename through setting
// target to 0;
a = CAP_NONE;
}
if (a < CAP_NONE) {
fprintf(stderr,
_
("reading unsupported target from options file\nresetting to target auto-detection\n"));
a = CAP_NONE;
}
if (a >= CAP_NONE)
app->multi_frame.target = a;
} else if (strcasecmp(token, "mf_codec") == 0) {
int n, a = -1;
for (n = CODEC_NONE; n < NUMCODECS; n++) {
if (strcasecmp(value, tCodecs[n].name) == 0)
a = n;
}
if (strcasecmp(value, "AUTO") == 0)
a = CODEC_NONE;
if (a < CODEC_NONE) {
fprintf(stderr,
_
("reading unsupported target codec (%i) from options file\nresetting to codec auto-detection\n"),
a);
a = CODEC_NONE;
}
app->multi_frame.targetCodec = a;
}
#ifdef HAVE_FFMPEG_AUDIO
else if (strcasecmp(token, "mf_au_codec") == 0) {
int n, a = -1;
for (n = AU_CODEC_NONE; n < NUMAUCODECS; n++) {
if (strcasecmp(value, tAuCodecs[n].name) == 0)
a = n;
}
if (strcasecmp(value, "AUTO") == 0)
a = AU_CODEC_NONE;
if (a < AU_CODEC_NONE) {
fprintf(stderr,
_
("reading unsupported target audio codec (%i) from options file\nresetting to audio codec auto-detection\n"),
a);
a = AU_CODEC_NONE;
}
app->multi_frame.au_targetCodec = a;
}
#endif // HAVE_FFMPEG_AUDIO
else if (strcasecmp(token, "mf_fps") == 0) {
app->multi_frame.fps =
xvc_get_int_from_float_string(value);
} else if (strcasecmp(token, "mf_max_time") == 0) {
app->multi_frame.time = atoi(value);
if (atof(value) > 0)
app->multi_frame.frames = 0;
} else if (strcasecmp(token, "mf_max_frames") == 0) {
app->multi_frame.frames = atoi(value);
if (atoi(value) > 0)
app->multi_frame.time = 0;
} else if (strcasecmp(token, "mf_start_no") == 0) {
app->multi_frame.start_no = atof(value);
} else if (strcasecmp(token, "mf_quality") == 0) {
app->multi_frame.quality = atoi(value);
if ((app->multi_frame.quality < 0)
|| (app->multi_frame.quality > 100))
app->multi_frame.quality = 75; /* reset to
* default */
}
#ifdef HAVE_FFMPEG_AUDIO
else if (strcasecmp(token, "mf_audio") == 0) {
if (strcasecmp(value, "0") == 0) {
app->multi_frame.audioWanted = 0;
} else if (strcasecmp(value, "1") == 0) {
app->multi_frame.audioWanted = 1;
} else {
fprintf(stderr,
_
("Invalid parameter 'audio: %s' in preferences file\n"),
value);
fprintf(stderr,
_
("Disabling audio capture unless overridden by command line\n"));
app->multi_frame.audioWanted = 0;
}
} else if (strcasecmp(token, "mf_audio_rate") == 0) {
if (value)
app->multi_frame.sndrate = atoi(value);
} else if (strcasecmp(token, "mf_audio_bits") == 0) {
if (value)
app->multi_frame.sndsize = atoi(value);
} else if (strcasecmp(token, "mf_audio_channels") == 0) {
if (value)
app->multi_frame.sndchannels = atoi(value);
}
#endif // HAVE_FFMPEG_AUDIO
else if (strcasecmp(token, "mf_animate_cmd") == 0) {
app->multi_frame.play_cmd = strdup(value);
} else if (strcasecmp(token, "mf_edit_cmd") == 0) {
app->multi_frame.edit_cmd = strdup(value);
} else if (strcasecmp(token, "mf_video_cmd") == 0) {
app->multi_frame.video_cmd = strdup(value);
}
#endif // USE_FFMPEG
}
}
} else { // !fp
return FALSE;
}
return TRUE;
}
syntax highlighted by Code2HTML, v. 0.9.1