View of xos/tools/makebin.c


XOS | Parent Directory | View | Download

/* Copyright (C) 2008  Emmanuel Varoquaux
 
   This file is part of XOS.
 
   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/>. */
 
/* Construit une archive pour XOS.
 *
 * Utilisation : makebin [file]...
 *   file  un fichier a ajouter
 * Options :
 *   -o filename  ecrire l'image dans le fichier filename, au lieu de la sortie
 *            standard. */
 
#define _GNU_SOURCE
 
#include "bin.h"
 
#include <getopt.h>
#include <error.h>
#include <string.h>
#include <stdlib.h>
#include <stdio.h>
#include <sys/stat.h>
#include <errno.h>
 
static void die(const char *s)
{
  error(EXIT_FAILURE, errno, s);
}
 
static void __attribute__ ((noreturn)) die_bad_usage()
{
  fprintf(stderr, "Try `%s --help' for more information.\n", program_invocation_name);
  exit(EXIT_FAILURE);
}
 
static void print_help()
{
  printf("Usage: %s [OPTION]... [FILE]...\n", program_invocation_name);
  printf("Create an archive for XOS.\n");
  printf("\n");
  printf("  -h, --help         display this help and exit\n");
  printf("  -o, --output=FILE  write output in FILE instead of standard output\n");
}
 
static inline void compute_checksum(bin_hdr_t *hdr)
{
  const unsigned char *pc;
 
  hdr->checksum = 0;
  for (pc = (const unsigned char *)hdr; pc != (const unsigned char *)&hdr->checksum; pc++)
    hdr->checksum += *pc;
}
 
static size_t write_header(const char *filename, off_t size, FILE *output)
{
  bin_hdr_t hdr;
  char *path;
 
  hdr.magic = BIN_MAGIC;
  hdr.size = size;
  memset(hdr.name, 0, BIN_NAME_LEN);
  if (!(path = strdup(filename)))
    die("strdup");
  strncpy(hdr.name, basename(path), BIN_NAME_LEN - 1);
  free(path);
  hdr.reserved = 0;
  compute_checksum(&hdr);
  if (fwrite(&hdr, sizeof (bin_hdr_t), 1, output) < 1)
    die("fwrite");
  return sizeof (bin_hdr_t);
}
 
static size_t write_data(FILE *fp, FILE *output)
{
  char buf[4096];
  size_t n;
  size_t l;
 
  n = 0;
  do {
    l = fread(buf, 1, sizeof buf, fp);
    if (ferror(fp))
      die("fread");
    if (l) {
      if (fwrite(buf, l, 1, output) < 1)
        die("fwrite");
      n += l;
    }
  }
  while (!feof(fp));
  return n;
}
 
static void write_trailer(FILE *output)
{
  bin_hdr_t hdr;
 
  memset(&hdr, 0, sizeof (bin_hdr_t));
  if (fwrite(&hdr, sizeof (bin_hdr_t), 1, output) < 1)
    die("fwrite");
}
 
static int add_file(const char *filename, FILE *output)
{
  static const char zeros[4] = {'\0', '\0', '\0', '\0'};
 
  struct stat statbuf;
  FILE *fp;
  size_t n;
 
  if (stat(filename, &statbuf) == -1) {
    error(0, errno, "cannot stat `%s'", filename);
    return 0;
  }
  if (S_ISDIR(statbuf.st_mode)) {
    error(0, 0, "omitting directory `%s'", filename);
    return 0;
  }
  if (!(fp = fopen(filename, "r"))) {
    error(0, errno, "cannot open `%s'", filename);
    return 0;
  }
 
  n = 0;
  n += write_header(filename, statbuf.st_size, output);
  n += write_data(fp, output);
  if (bin_align(n) != n)
    if (fwrite(zeros, bin_align(n) - n, 1, output) < 1)
      die("fwrite");
 
  fclose(fp);
 
  return 1;
}
 
static int makebin(char *const filenames[], FILE *output)
{
  int status;
 
  status = 1;
  while (*filenames)
    if (!add_file(*filenames++, output))
      status = 0;
  write_trailer(output);
  return status;
}
 
int main(int argc, char **argv)
{
  static const struct option longopts[] = {
    {"help", no_argument, NULL, 'h'},
    {"output", required_argument, NULL, 'o'},
    {NULL, 0, NULL, 0}
  };
 
  char *output_filename;
  FILE *output;
  int status;
  int c;
 
  /* options par defaut */
  output_filename = NULL;
 
  /* lecture des options de la ligne de commande */
  while ((c = getopt_long(argc, argv, "ho:", longopts, NULL)) != -1)
    switch (c) {
    case 'h':
      print_help();
      return EXIT_SUCCESS;
    case 'o':
      if (output_filename)
        free(output_filename);
      if (!(output_filename = strdup(optarg)))
        die("strdup");
      break;
    case '?':
      die_bad_usage();
    }
 
  if (output_filename) {
    if (!(output = fopen(output_filename, "w+")))
      die(output_filename);
    free(output_filename);
  }
  else
    output = stdout;
 
  status = makebin(argv + optind, output);
 
  if (output != stdout)
    fclose(output);
 
  return status ? EXIT_SUCCESS : EXIT_FAILURE;
}