/* main.c - Main routine from icotool
*
* Copyright (C) 1998 Oskar Liljeblad
*
* 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 3 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, see <http://www.gnu.org/licenses/>.
*/
#include <config.h>
#include <unistd.h> /* POSIX */
#include <errno.h> /* C89 */
#include <stdio.h> /* C89 */
#include <getopt.h> /* Gnulib/GNU Libc */
#include <string.h> /* C89 */
#include <stdlib.h> /* C89 */
#ifdef HAVE_LOCALE_H
# include <locale.h> /* Solaris */
#endif
#include "gettext.h"
#include "configmake.h"
#define _(s) gettext(s)
#define N_(s) gettext_noop(s)
#include "progname.h" /* Gnulib */
#include "version-etc.h" /* Gnulib */
#include "xalloc.h" /* Gnulib */
#include "common/error.h"
#include "common/strbuf.h"
#include "common/string-utils.h"
#include "common/intutil.h"
#include "common/io-utils.h"
#include "icotool.h"
#define PROGRAM "icotool"
static int32_t image_index = -1;
static int32_t width = -1;
static int32_t height = -1;
static int32_t bitdepth = -1;
/*static uint32_t minbitdepth = 1;*/
static int32_t palettesize = -1;
static int32_t hotspot_x = 0;
static int32_t hotspot_y = 0;
static int hotspot_x_set = 0;
static int hotspot_y_set = 0;
static int32_t alpha_threshold = 127;
static bool icon_only = false;
static bool cursor_only = false;
static char *output = NULL;
const char version_etc_copyright[] = "Copyright (C) 1998 Oskar Liljeblad";
enum {
VERSION_OPT = 1000,
HELP_OPT,
ICON_OPT,
CURSOR_OPT,
};
static const char *short_opts = "-xlco:i:w:h:p:b:X:Y:t:r:";
static struct option long_opts[] = {
{ "extract", no_argument, NULL, 'x' },
{ "list", no_argument, NULL, 'l' },
{ "create", no_argument, NULL, 'c' },
{ "version", no_argument, NULL, VERSION_OPT },
{ "help", no_argument, NULL, HELP_OPT },
{ "output", required_argument, NULL, 'o' },
{ "index", required_argument, NULL, 'i' },
{ "width", required_argument, NULL, 'w' },
{ "height", required_argument, NULL, 'h' },
{ "palette-size", required_argument, NULL, 'p' },
{ "bit-depth", required_argument, NULL, 'b' },
/*{ "min-bit-depth", required_argument, NULL, 'm' },*/
{ "hotspot-x", required_argument, NULL, 'X' },
{ "hotspot-y", required_argument, NULL, 'Y' },
{ "alpha-threshold", required_argument, NULL, 't' },
{ "icon", no_argument, NULL, ICON_OPT },
{ "cursor", no_argument, NULL, CURSOR_OPT },
{ "raw", required_argument, NULL, 'r' },
{ 0, 0, 0, 0 }
};
static bool
filter(int i, int w, int h, int bd, int ps, bool icon, int hx, int hy)
{
if (image_index != -1 && i != image_index)
return false;
if (width != -1 && w != width)
return false;
if (height != -1 && h != height)
return false;
if (bitdepth != -1 && bd != bitdepth)
return false;
/*if (bd < minbitdepth)
return false;*/
if (palettesize != -1 && ps != palettesize)
return false;
if ((icon_only && !icon) || (cursor_only && icon))
return false;
if (hotspot_x_set && hx != hotspot_x)
return false;
if (hotspot_y_set && hy != hotspot_y)
return false;
return true;
}
static FILE *
create_outfile_gen(char **out)
{
if (output != NULL) {
*out = xstrdup(output);
return fopen(output, "wb");
}
if (isatty(STDOUT_FILENO))
die(_("refusing to write binary data to terminal"));
*out = xstrdup(_("(standard out)"));
return stdout;
}
static FILE *
extract_outfile_gen(const char *inname, char **outname_ptr, int w, int h, int bc, int i)
{
if (output == NULL || is_directory(output)) {
StrBuf *outname;
const char *inbase;
outname = strbuf_new();
if (output != NULL) {
strbuf_append(outname, output);
if (!ends_with(output, "/"))
strbuf_append(outname, "/");
}
inbase = strrchr(inname, '/');
inbase = (inbase == NULL ? inname : inbase+1);
if (ends_with_nocase(inbase, ".ico") || ends_with_nocase(inbase, ".cur")) {
strbuf_append_substring(outname, inbase, 0, strlen(inbase)-4);
} else {
strbuf_append(outname, inbase);
}
strbuf_appendf(outname, "_%d_%dx%dx%d.png", i, w, h, bc);
*outname_ptr = strbuf_free_to_string(outname);
return fopen(*outname_ptr, "wb");
}
else if (strcmp(output, "-") == 0) {
*outname_ptr = xstrdup(_("(standard out)"));
return stdout;
}
*outname_ptr = xstrdup(output);
return fopen(output, "wb");
}
static void
display_help(void)
{
printf(_("Usage: %s [OPTION]... [FILE]...\n"), program_name);
printf(_("Convert and create Win32 icon (.ico) and cursor (.cur) files.\n"));
printf(_("\nCommands:\n"));
printf(_(" -x, --extract extract images from files\n"));
printf(_(" -l, --list print a list of images in files\n"));
printf(_(" -c, --create create an icon file from specified files\n"));
printf(_(" --help display this help and exit\n"));
printf(_(" --version output version information and exit\n"));
printf(_("\nOptions:\n"));
printf(_(" -i, --index=NUMBER match index of image (first is 1)\n"));
printf(_(" -w, --width=PIXELS match width of image\n"));
printf(_(" -h, --height=PIXELS match height of image\n"));
printf(_(" -p, --palette-size=COUNT match number of colors in palette (or 0)\n"));
printf(_(" -b, --bit-depth=COUNT match or set number of bits per pixel\n"));
/*printf(_(" -m, --min-bit-depth=COUNT match or set minimum number of bits per pixel\n"));*/
printf(_(" -X, --hotspot-x=COORD match or set cursor hotspot x-coordinate\n"));
printf(_(" -Y, --hotspot-y=COORD match or set cursor hotspot y-coordinate\n"));
printf(_(" -t, --alpha-threshold=LEVEL highest level in alpha channel indicating\n"
" transparent image portions (default is 127)\n"));
printf(_(" -r, --raw=FILENAME store input file as raw PNG (\"Vista icons\")\n"));
printf(_(" --icon match icons only\n"));
printf(_(" --cursor match cursors only\n"));
printf(_(" -o, --output=PATH where to place extracted files\n"));
printf(_("\n"));
printf(_("Report bugs to <%s>.\n"), PACKAGE_BUGREPORT);
}
static bool
open_file_or_stdin(char *name, FILE **outfile, const char **outname)
{
if (strcmp(name, "-") == 0) {
*outfile = stdin;
*outname = "(standard in)";
} else {
*outfile = fopen(name, "rb");
*outname = name;
if (*outfile == NULL) {
warn("%s: cannot open file", name);
return false;
}
}
return true;
}
static void
input_files_init (InputFiles *files)
{
memset (files, 0, sizeof (InputFiles));
}
static void
input_files_add (InputFiles *files, char* name, int32_t bit_count, int32_t file_hotspot_x, int32_t file_hotspot_y)
{
files->files = realloc (files->files, (files->count+1)*sizeof (InputFile));
files->files[files->count].name = name;
files->files[files->count].is_raw = false;
files->files[files->count].bit_count = bitdepth;
files->files[files->count].hotspot_x = file_hotspot_x;
files->files[files->count].hotspot_y = file_hotspot_y;
files->count++;
}
static void
input_files_add_raw (InputFiles *files, char* name, int32_t file_hotspot_x, int32_t file_hotspot_y)
{
files->files = realloc (files->files, (files->count+1)*sizeof (InputFile));
files->files[files->count].name = name;
files->files[files->count].is_raw = true;
files->files[files->count].hotspot_x = file_hotspot_x;
files->files[files->count].hotspot_y = file_hotspot_y;
files->count++;
}
int
main(int argc, char **argv)
{
int c;
bool list_mode = false;
bool extract_mode = false;
bool create_mode = false;
FILE *in;
const char *inname;
size_t i;
InputFiles files;
int num_bitdepth = 0;
set_program_name(argv[0]);
input_files_init (&files);
#ifdef ENABLE_NLS
if (setlocale(LC_ALL, "") == NULL)
warn(_("%s: cannot set locale: %s"), program_name, errstr);
if (bindtextdomain(PACKAGE, LOCALEDIR) == NULL)
warn(_("%s: bindtextdomain failed: %s"), program_name, errstr);
if (textdomain(PACKAGE) == NULL)
warn(_("%s: cannot set message domain: %s"), program_name, errstr);
#endif
while ((c = getopt_long(argc, argv, short_opts, long_opts, NULL)) != -1) {
switch (c) {
case 1:
input_files_add (&files, optarg, bitdepth, hotspot_x, hotspot_y);
break;
case 'x':
extract_mode = true;
break;
case 'l':
list_mode = true;
break;
case 'c':
create_mode = true;
break;
case VERSION_OPT:
version_etc(stdout, PROGRAM, PACKAGE, VERSION, "Oskar Liljeblad", NULL);
exit(0);
case HELP_OPT:
display_help();
exit(0);
case 'o':
output = optarg;
break;
case 'i':
if (!parse_int32(optarg, &image_index) || image_index < 0)
die(_("invalid index value: %s"), optarg);
break;
case 'w':
if (!parse_int32(optarg, &width) || width < 0)
die(_("invalid width value: %s"), optarg);
break;
case 'h':
if (!parse_int32(optarg, &height) || height < 0)
die(_("invalid height value: %s"), optarg);
break;
case 'p':
if (!parse_int32(optarg, &palettesize) || palettesize < 0)
die(_("invalid palette-size value: %s"), optarg);
break;
case 'b':
if (!parse_int32(optarg, &bitdepth) || bitdepth < 0)
die(_("invalid bit-depth value: %s"), optarg);
++num_bitdepth;
break;
/*case 'm':
if (!parse_uint32(optarg, &minbitdepth))
die(_("invalid minimum bit-depth value: %s"), optarg);
break;*/
case 'X':
if (!parse_int32(optarg, &hotspot_x) || hotspot_x < 0)
die(_("invalid hotspot-x value: %s"), optarg);
++hotspot_x_set;
break;
case 'Y':
if (!parse_int32(optarg, &hotspot_y) || hotspot_y < 0)
die(_("invalid hotspot-y value: %s"), optarg);
++hotspot_y_set;
break;
case 't':
if (!parse_int32(optarg, &alpha_threshold) || alpha_threshold < 0)
die(_("invalid alpha-threshold value: %s"), optarg);
break;
case 'r':
input_files_add_raw (&files, optarg, hotspot_x, hotspot_y);
break;
case ICON_OPT:
icon_only = true;
break;
case CURSOR_OPT:
cursor_only = true;
break;
case '?':
exit(1);
}
}
/* Handle options after '--' */
for (c = optind ; c < argc ; c++) {
input_files_add (&files, argv[c], bitdepth, hotspot_x, hotspot_y);
}
if (extract_mode + create_mode + list_mode > 1)
die(_("multiple commands specified"));
if (extract_mode + create_mode + list_mode == 0) {
warn(_("missing argument"));
display_help();
exit (1);
}
if (icon_only && cursor_only)
die(_("only one of --icon and --cursor may be specified"));
if (list_mode) {
if (files.count <= 0)
die(_("missing file argument"));
for (i = 0 ; i < files.count ; i++) {
if (open_file_or_stdin(files.files[i].name, &in, &inname)) {
if (!extract_icons(in, inname, true, NULL, filter))
exit(1);
if (in != stdin)
fclose(in);
}
}
}
if (extract_mode) {
if (files.count <= 0)
die(_("missing arguments"));
for (i = 0 ; i < files.count ; i++) {
int matched;
if (open_file_or_stdin(files.files[i].name, &in, &inname)) {
matched = extract_icons(in, inname, false, extract_outfile_gen, filter);
if (matched == -1)
exit(1);
if (matched == 0)
fprintf(stderr, _("%s: no images matched\n"), inname);
if (in != stdin)
fclose(in);
}
}
}
if (create_mode) {
if (files.count <= 0)
die(_("missing arguments"));
if (num_bitdepth == 1) {
/* Change bitdepth to the single value provided (for compatibility) */
for (i = 0 ; i < files.count ; i++) {
files.files[i].bit_count = bitdepth;
}
} else if (num_bitdepth > 1) {
/* Warn if bitdepth is unset */
for (i = 0 ; i < files.count ; i++) {
if (files.files[i].is_raw) continue;
if (files.files[i].bit_count < 0) {
fprintf(stderr, _("%s: No bit-depth given\n"), files.files[i].name);
}
}
}
if (hotspot_x_set == 1) {
/* Change hotspot to the single value provided (for compatibility) */
for (i = 0 ; i < files.count ; i++) {
files.files[i].hotspot_x = hotspot_x;
}
}
if (hotspot_y_set == 1) {
/* Change hotspot to the single value provided (for compatibility) */
for (i = 0 ; i < files.count ; i++) {
files.files[i].hotspot_y = hotspot_y;
}
}
if (!create_icon(&files, create_outfile_gen, (icon_only ? true : !cursor_only), alpha_threshold))
exit(1);
}
exit(0);
}