blob: 3f42b9db87cfeb9b6368cb4199d9a28416cf2ad3 [file] [log] [blame]
/* SAPfs
* Version 0.9.4
* Copyright (C) 2001-2003 Eric Botcazou
*
* 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <time.h>
#include <dirent.h>
#include <sys/stat.h>
#include "libsap.h"
#define SAPFS_VERSION_STR "0.9.4"
#ifdef linux
#define SAPFS_PLATFORM_STR "Linux"
#else
#define SAPFS_PLATFORM_STR "MSDOS"
#endif
#define FILENAME_LENGTH 512
/* ugly hack to support French accents */
#ifdef linux
static char eacute[] = "é";
static char egrave[] = "è";
static char agrave[] = "à";
static char ugrave[] = "ù";
#else
static char eacute[] = "‚";
static char egrave[] = "Š";
static char agrave[] = "…";
static char ugrave[] = "—";
#endif
/* PrintErrorMessage:
* Prints the error message corresponding to the specified error.
*/
static void PrintErrorMessage(int errno, const char str[])
{
switch (errno) {
case SAP_EBADF:
fprintf(stderr, "Erreur: le fichier %s n'est pas une archive SAP valide.\n", str);
break;
case SAP_EFBIG:
fprintf(stderr, "Erreur: le fichier %s est de taille trop importante.\n", str);
break;
case SAP_ENFILE:
fprintf(stderr, "Erreur: le fichier %s est vide.\n", str);
break;
case SAP_ENOENT:
fprintf(stderr, "Erreur: le fichier %s est introuvable.\n", str);
break;
case SAP_ENOSPC:
fprintf(stderr, "Erreur: le r%spertoire de l'archive est plein.\n", eacute);
break;
case SAP_EPERM:
fprintf(stderr, "Erreur: impossible de cr%ser le fichier %s.\n", eacute, str);
break;
}
}
/* put_separator:
* Helper function to add a missing directory separator.
*/
static void put_separator(char filename[])
{
int last;
last = strlen(filename) - 1;
if (filename[last] != '/')
filename[last + 1] = '/';
}
/* CreateEmptyArchive:
* Creates a new archive using the specified name.
*/
static int CreateEmptyArchive(const char sap_name[], int format, int capacity)
{
sapID sap_file;
if ((sap_file=sap_CreateArchive(sap_name, format)) == SAP_ERROR) {
PrintErrorMessage(sap_errno, sap_name);
return 1;
}
sap_FormatArchive(sap_file, capacity);
sap_CloseArchive(sap_file);
return 0;
}
/* FormatArchive:
* Formats the specified already existing archive.
*/
static int FormatArchive(const char sap_name[], int capacity)
{
sapID sap_file;
int format;
if ((sap_file=sap_OpenArchive(sap_name, &format)) == SAP_ERROR) {
PrintErrorMessage(sap_errno, sap_name);
return 1;
}
sap_FormatArchive(sap_file, capacity);
sap_CloseArchive(sap_file);
return 0;
}
/* VerifyArchive:
* Verifies one or more sectors from the specified archive.
*/
static int VerifyArchive(const char sap_name[], int track, int sect)
{
sapsector_t sapsector;
sapID sap_file;
int format, ntracks, s, t, flag;
if ((sap_file=sap_OpenArchive(sap_name, &format)) == SAP_ERROR) {
PrintErrorMessage(sap_errno, sap_name);
return 1;
}
ntracks = (format == SAP_FORMAT1 ? SAP_NTRACKS1 : SAP_NTRACKS2);
/* check track number */
if (track<0) {
t = 0;
track = ntracks-1;
}
else {
if (track>=ntracks) {
fprintf(stderr, "Erreur: num%sro de piste invalide.\n", eacute);
sap_CloseArchive(sap_file);
return 1;
}
t = track;
}
/* check sector number */
if (sect<0) {
s = 1;
sect = SAP_NSECTS;
}
else {
if ((sect<1) || (sect>SAP_NSECTS)) {
fprintf(stderr, "Erreur: num%sro de secteur invalide.\n", eacute);
sap_CloseArchive(sap_file);
return 1;
}
s = sect;
}
while (t <= track) {
while (s <= sect) {
flag = sap_ReadSector(sap_file, t, s, &sapsector);
if (flag != SAP_OK) {
printf("track %d sector %02d: ", t, s);
if (flag & SAP_NO_STD_FMT)
printf("<format=%d> ", sapsector.format);
if (flag & SAP_PROTECTED)
printf("<protection=%d> ", sapsector.protection);
if (flag & SAP_BAD_SECTOR)
printf("<track=%d sector=%d> ", sapsector.track, sapsector.sector);
if (flag & SAP_CRC_ERROR)
printf("<CRC error>");
printf("\n");
}
s++;
} /* end of while (s <= sect) */
s = 1;
t++;
} /* end of while (t <= track ) */
sap_CloseArchive(sap_file);
return 0;
}
/* DumpArchive:
* Dumps one or more sectors from the specified archive.
*/
static int DumpArchive(const char sap_name[], int track, int sect)
{
sapsector_t sapsector;
unsigned char c;
sapID sap_file;
int format, ntracks, i,j,s,t;
if ((sap_file=sap_OpenArchive(sap_name, &format)) == SAP_ERROR) {
PrintErrorMessage(sap_errno, sap_name);
return 1;
}
ntracks = (format == SAP_FORMAT1 ? SAP_NTRACKS1 : SAP_NTRACKS2);
/* check track number */
if (track<0) {
t = 0;
track = ntracks-1;
}
else {
if (track>=ntracks) {
fprintf(stderr, "Erreur: num%sro de piste invalide.\n", eacute);
sap_CloseArchive(sap_file);
return 1;
}
t = track;
}
/* check sector number */
if (sect<0) {
s = 1;
sect = SAP_NSECTS;
}
else {
if ((sect<1) || (sect>SAP_NSECTS)) {
fprintf(stderr, "Erreur: num%sro de secteur invalide.\n", eacute);
sap_CloseArchive(sap_file);
return 1;
}
s = sect;
}
while (t <= track) {
while (s <= sect) {
sap_ReadSector(sap_file, t, s, &sapsector);
printf("track id: %02d\n", sapsector.track);
printf("sector id: %02d\n", sapsector.sector);
printf("format: %d\n", sapsector.format);
printf("protection: %d\n", sapsector.protection);
printf("data:\n");
for (i=0; i< (format == SAP_FORMAT1 ? 16 : 8); i++) {
for (j=0; j<16; j++) {
c = sapsector.data[i*16+j];
printf("%02X ", c);
}
for (j=0; j<16; j++) {
c = sapsector.data[i*16+j];
if ((c>=32) && (c<=125))
putchar(c);
else
putchar('.');
}
printf("\n");
}
s++;
} /* end of while (s <= sect) */
s = 1;
t++;
} /* end of while (t <= track ) */
sap_CloseArchive(sap_file);
return 0;
}
/* ListArchive:
* Lists the files contained in the specified archive.
*/
static int ListArchive(const char sap_name[])
{
char buffer[4096];
sapID sap_file;
int format;
if ((sap_file=sap_OpenArchive(sap_name, &format)) == SAP_ERROR) {
PrintErrorMessage(sap_errno, sap_name);
return 1;
}
sap_ListArchive(sap_file, buffer, sizeof(buffer));
puts(buffer);
sap_CloseArchive(sap_file);
return 0;
}
/* PrintFileInfo:
* Prints the info for the specified file.
*/
static int PrintFileInfo(const char sap_name[], char filename[])
{
int format, i, ret = 0;
sapID sap_file;
sapfileinfo_t info;
char tmp[64];
if ((sap_file=sap_OpenArchive(sap_name, &format)) == SAP_ERROR) {
PrintErrorMessage(sap_errno, sap_name);
return 1;
}
if (sap_GetFileInfo(sap_file, filename, &info) == SAP_ERROR) {
PrintErrorMessage(sap_errno, filename);
ret = 1;
}
else {
printf("name: %s\n", filename);
printf("size: %d bytes\n", info.size);
printf("file type: %d\n", info.file_type);
printf("data type: %d\n", info.data_type);
if (info.date) {
strftime(tmp, sizeof(tmp), "%m/%d/%Y %H:%M ", localtime(&info.date));
printf("date: %s\n", tmp);
}
if (info.comment[0])
printf("comment: %s\n", info.comment);
printf("blocks: ");
for (i=0; i<info.nblocks; i++)
printf("%d ", info.block[i]);
printf("\n");
free(info.block);
}
sap_CloseArchive(sap_file);
return ret;
}
/* ExtractFile:
* Extracts one or more files from the specified archive.
*/
static int ExtractFile(const char sap_name[], char *filename[], int nfiles)
{
int format, i, len, ret = 0;
sapID sap_file;
if ((sap_file=sap_OpenArchive(sap_name, &format)) == SAP_ERROR) {
PrintErrorMessage(sap_errno, sap_name);
return 1;
}
for (i=0; i<nfiles; i++) {
len = sap_ExtractFile(sap_file, filename[i]);
if (len == 0) {
PrintErrorMessage(sap_errno, filename[i]);
ret = 1;
break;
}
}
sap_CloseArchive(sap_file);
return ret;
}
/* AddFile:
* Adds one or more files to the specified archive.
*/
static int AddFile(const char sap_name[], char *filename[], int nfiles)
{
char pathname[FILENAME_LENGTH];
int format, i, len = 0, ret = 0;
struct stat s;
struct dirent *entry;
sapID sap_file;
DIR *dir;
if ((sap_file=sap_OpenArchive(sap_name, &format)) == SAP_ERROR) {
PrintErrorMessage(sap_errno, sap_name);
return 1;
}
for (i=0; i<nfiles; i++) {
/* get file statistics */
if (stat(filename[i], &s) != 0) {
fprintf(stderr, "Erreur: le fichier %s est introuvable.\n", filename[i]);
ret = 1;
break;
}
if (S_ISDIR(s.st_mode)) { /* directory? */
if ((dir=opendir(filename[i])) == NULL) {
fprintf(stderr, "Erreur: le r%spertoire %s est inaccessible en lecture.\n", eacute, filename[i]);
ret = 1;
break;
}
/* add every entry in turn */
while ((entry = readdir(dir)) != NULL) {
if (strcmp(entry->d_name, ".") && strcmp(entry->d_name, "..")) {
strncpy(pathname, filename[i], FILENAME_LENGTH - 1);
put_separator(pathname);
strncat(pathname, entry->d_name, FILENAME_LENGTH - strlen(pathname) - 1);
len = sap_AddFile(sap_file, pathname);
if (len == 0) {
PrintErrorMessage(sap_errno, pathname);
ret = 1;
break;
}
}
}
closedir(dir);
}
else {
len = sap_AddFile(sap_file, filename[i]);
}
if (len == 0) {
if (ret == 0) {
PrintErrorMessage(sap_errno, filename[i]);
ret = 1;
}
break;
}
}
sap_CloseArchive(sap_file);
return ret;
}
/* DeleteFile:
* Deletes one or more files from the specified archive.
*/
static int DeleteFile(const char sap_name[], char *filename[], int nfiles)
{
int format, i, len, ret = 0;
sapID sap_file;
if ((sap_file=sap_OpenArchive(sap_name, &format)) == SAP_ERROR) {
PrintErrorMessage(sap_errno, sap_name);
return 1;
}
for (i=0; i<nfiles; i++) {
len = sap_DeleteFile(sap_file, filename[i]);
if (len == 0) {
PrintErrorMessage(sap_errno, filename[i]);
ret = 1;
break;
}
}
sap_CloseArchive(sap_file);
return ret;
}
/* CopyArchive:
* Copies one or more sectors from the source archive to the dest archive.
*/
static int CopyArchive(const char src_name[], const char dest_name[], int track, int sect)
{
sapsector_t sapsector;
sapID src_file, dest_file;
int src_format, dest_format, ntracks, s, t;
if ((src_file=sap_OpenArchive(src_name, &src_format)) == SAP_ERROR) {
PrintErrorMessage(sap_errno, src_name);
return 1;
}
if ((dest_file=sap_OpenArchive(dest_name, &dest_format)) == SAP_ERROR) {
PrintErrorMessage(sap_errno, dest_name);
sap_CloseArchive(src_file);
return 1;
}
if (src_format != dest_format) {
fprintf(stderr, "Erreur: archives de format diff%srent.\n", eacute);
goto Error;
}
ntracks = (src_format == SAP_FORMAT1 ? SAP_NTRACKS1 : SAP_NTRACKS2);
/* check track number */
if (track<0) {
t = 0;
track = ntracks-1;
}
else {
if (track>=ntracks) {
fprintf(stderr, "Erreur: num%sro de piste invalide.\n", eacute);
goto Error;
}
t = track;
}
/* check sector number */
if (sect<0) {
s = 1;
sect = SAP_NSECTS;
}
else {
if ((sect<1) || (sect>SAP_NSECTS)) {
fprintf(stderr, "Erreur: num%sro de secteur invalide.\n", eacute);
goto Error;
}
s = sect;
}
while (t <= track) {
while (s <= sect) {
sap_ReadSector(src_file, t, s, &sapsector);
sap_WriteSector(dest_file, t, s, &sapsector);
s++;
} /* end of while (s <= sect) */
s = 1;
t++;
} /* end of while (t <= track ) */
sap_CloseArchive(dest_file);
sap_CloseArchive(src_file);
return 0;
Error:
sap_CloseArchive(src_file);
sap_CloseArchive(dest_file);
return 1;
}
/* MoveSector:
* Copies with deplacement one sector from the source archive to the dest archive.
*/
static int MoveSector(const char src_name[], int src_track, int src_sect,
const char dest_name[], int dest_track, int dest_sect)
{
sapsector_t sapsector;
sapID src_file, dest_file;
int src_format, dest_format, ntracks;
if ((src_file=sap_OpenArchive(src_name, &src_format)) == SAP_ERROR) {
PrintErrorMessage(sap_errno, src_name);
return 1;
}
if ((dest_file=sap_OpenArchive(dest_name, &dest_format)) == SAP_ERROR) {
PrintErrorMessage(sap_errno, dest_name);
sap_CloseArchive(src_file);
return 1;
}
if (src_format != dest_format) {
fprintf(stderr, "Erreur: archives de format diff%srent.\n", eacute);
goto Error;
}
ntracks = (src_format == SAP_FORMAT1 ? SAP_NTRACKS1 : SAP_NTRACKS2);
/* check track number */
if ((src_track<0) || (src_track>=ntracks)) {
fprintf(stderr, "Erreur: num%sro de piste invalide.\n", eacute);
goto Error;
}
/* check sector number */
if ((src_sect<1) || (src_sect>SAP_NSECTS)) {
fprintf(stderr, "Erreur: num%sro de secteur invalide.\n", eacute);
goto Error;
}
/* check track number */
if ((dest_track<0) || (dest_track>=ntracks)) {
fprintf(stderr, "Erreur: num%sro de piste invalide.\n", eacute);
goto Error;
}
/* check sector number */
if ((dest_sect<1) || (dest_sect>SAP_NSECTS)) {
fprintf(stderr, "Erreur: num%sro de secteur invalide.\n", eacute);
goto Error;
}
sap_ReadSector(src_file, src_track, src_sect, &sapsector);
sapsector.track = dest_track;
sapsector.sector = dest_sect;
sap_WriteSector(dest_file, dest_track, dest_sect, &sapsector);
sap_CloseArchive(src_file);
sap_CloseArchive(dest_file);
return 0;
Error:
sap_CloseArchive(src_file);
sap_CloseArchive(dest_file);
return 1;
}
#define COMMAND_MAX 14
static char *short_command[] = { "-h", "-v", "-c", "-f", "-w", "-u",
"-t", "-i", "-x", "-y", "-a", "-d", "-k", "-m" };
static char *long_command[] = { "--help", "--version", "--create", "--format", "--verify", "--dump",
"--list", "--info", "--extract", "--extract-all", "--add", "--delete",
"--copy", "--move" };
/* usage:
* Displays the commands and quits.
*/
static void usage(const char prog_name[])
{
fprintf(stderr, "Usage: %s -h --help | -v --version | -c --create | -f --format\n", prog_name);
fprintf(stderr, " -w --verify | -d --dump | -t --list | -i --info\n");
fprintf(stderr, " -x --extract[-all] | -a --add | -d --delete\n");
exit(EXIT_FAILURE);
}
/* main:
* Entry point for the program.
*/
int main(int argc, char *argv[])
{
int i, ret = 0;
char *star = "*";
if (argc < 2) /* no argument? */
usage(argv[0]);
if (argv[1][0] == '-') {
switch (argv[1][1]) {
case '-': /* long commands */
for (i=0; i<COMMAND_MAX; i++) {
if (strcmp(argv[1], long_command[i]) == 0) {
argv[1] = short_command[i];
return main(argc, argv);
}
}
usage(argv[0]);
case 'h': /* help */
printf("SAPfs est un outil de manipulation des archives SAP qui permet de r%saliser\n", eacute);
printf("sur ces archives les op%srations naturelles d'un syst%sme de fichiers.\n\n", eacute, egrave);
printf("Usage:\n");
printf(" %s commande1 archive.sap [fichier...] [piste] [sect]\n", argv[0]);
printf(" %s commande2 archive.sap [nb pistes] [densit%s]\n", argv[0], eacute);
printf(" %s commande3 archive.sap archive2.sap [piste] [sect]\n", argv[0]);
printf(" %s commande4 archive1.sap piste sect archive2.sap piste sect\n", argv[0]);
printf("o%s la commande1 est prise parmi les suivantes:\n", ugrave);
printf(" -h, --help affiche cette aide\n");
printf(" -v, --version affiche la version du programme\n");
printf(" -w, --verify effectue une v%srification d'un ou plusieurs secteurs\n", eacute);
printf(" -u, --dump affiche le contenu d'un ou plusieurs secteurs\n");
printf(" -t, --list affiche la liste des fichiers de l'archive SAP\n");
printf(" -i, --info affiche les informations relatives %s un fichier\n", agrave);
printf(" -x, --extract extrait un ou plusieurs fichiers de l'achive SAP\n");
printf(" --extract-all extrait tous les fichiers de l'archive SAP\n");
printf(" -a, --add ajoute un ou plusieurs fichiers %s l'archive SAP\n", agrave);
printf(" -d, --delete d%struit un ou plusieurs fichiers de l'archive SAP\n", eacute);
printf("et o%s la commande2 est prise parmi les suivantes:\n", ugrave);
printf(" -c, --create cr%se une archive SAP vide\n", eacute);
printf(" -f, --format formate une archive SAP\n");
printf("et o%s la commande3 est prise parmi les suivantes:\n", ugrave);
printf(" -k, --copy copie un ou plusieurs secteurs\n");
printf("et o%s la commande4 est prise parmi les suivantes:\n", ugrave);
printf(" -m, --move copie un secteur avec d%splacement\n", eacute);
break;
case 'v': /* version */
printf("SAPfs version "SAPFS_VERSION_STR" pour "SAPFS_PLATFORM_STR", copyright (C) 2001-2003 Eric Botcazou.\n");
break;
case 'c': /* create */
if (argc < 3)
usage(argv[0]);
if (argc < 4) {
ret = CreateEmptyArchive(argv[2], SAP_FORMAT1, SAP_TRK80);
}
else if (argc == 4){
if (atoi(argv[3]) == 40)
ret = CreateEmptyArchive(argv[2], SAP_FORMAT1, SAP_TRK40);
else if (atoi(argv[3]) == 80)
ret = CreateEmptyArchive(argv[2], SAP_FORMAT1, SAP_TRK80);
else
ret = 1;
}
else {
if (atoi(argv[3]) == 40)
ret = CreateEmptyArchive(argv[2], (atoi(argv[4]) == 1 ? SAP_FORMAT2 : SAP_FORMAT1), SAP_TRK40);
else if (atoi(argv[3]) == 80)
ret = CreateEmptyArchive(argv[2], (atoi(argv[4]) == 1 ? SAP_FORMAT2 : SAP_FORMAT1), SAP_TRK80);
else
ret = 1;
}
break;
case 'f': /* format */
if (argc < 3)
usage(argv[0]);
if (argc < 4) {
ret = FormatArchive(argv[2], SAP_TRK80);
}
else {
if (atoi(argv[3]) == 80)
ret = FormatArchive(argv[2], SAP_TRK80);
else if (atoi(argv[3]) == 40)
ret = FormatArchive(argv[2], SAP_TRK40);
else
ret = 1;
}
break;
case 'w': /* verify */
if (argc < 3)
usage(argv[0]);
if (argc < 4)
ret = VerifyArchive(argv[2], -1, -1);
else if (argc < 5)
ret = VerifyArchive(argv[2], atoi(argv[3]), -1);
else
ret = VerifyArchive(argv[2], atoi(argv[3]), atoi(argv[4]));
break;
case 'u': /* dump */
if (argc < 3)
usage(argv[0]);
if (argc < 4)
ret = DumpArchive(argv[2], -1, -1);
else if (argc < 5)
ret = DumpArchive(argv[2], atoi(argv[3]), -1);
else
ret = DumpArchive(argv[2], atoi(argv[3]), atoi(argv[4]));
break;
case 't': /* list */
if (argc < 3)
usage(argv[0]);
ret = ListArchive(argv[2]);
break;
case 'i': /* info */
if (argc < 4)
usage(argv[0]);
ret = PrintFileInfo(argv[2], argv[3]);
break;
case 'x': /* extract */
if (argc < 4)
usage(argv[0]);
ret = ExtractFile(argv[2], argv+3, argc-3);
break;
case 'y': /* extract all */
if (argc < 3)
usage(argv[0]);
ret = ExtractFile(argv[2], &star, 1);
break;
case 'a': /* add */
if (argc < 4)
usage(argv[0]);
ret = AddFile(argv[2], argv+3, argc-3);
break;
case 'd': /* delete */
if (argc < 4)
usage(argv[0]);
ret = DeleteFile(argv[2], argv+3, argc-3);
break;
case 'k': /* copy */
if (argc < 4)
usage(argv[0]);
if (argc < 5)
ret = CopyArchive(argv[2], argv[3], -1, -1);
else if (argc < 6)
ret = CopyArchive(argv[2], argv[3], atoi(argv[4]), -1);
else
ret = CopyArchive(argv[2], argv[3], atoi(argv[4]), atoi(argv[5]));
break;
case 'm': /* move */
if (argc != 8)
usage(argv[0]);
ret = MoveSector(argv[2], atoi(argv[3]), atoi(argv[4]), argv[5], atoi(argv[6]), atoi(argv[7]));
break;
default:
usage(argv[0]);
} /* end of switch */
}
return ret;
}