diff options
Diffstat (limited to 'fs/fdos')
-rwxr-xr-x | fs/fdos/Makefile | 49 | ||||
-rwxr-xr-x | fs/fdos/dev.c | 195 | ||||
-rwxr-xr-x | fs/fdos/dos.h | 175 | ||||
-rwxr-xr-x | fs/fdos/fat.c | 142 | ||||
-rwxr-xr-x | fs/fdos/fdos.c | 175 | ||||
-rwxr-xr-x | fs/fdos/fdos.h | 116 | ||||
-rwxr-xr-x | fs/fdos/fs.c | 118 | ||||
-rwxr-xr-x | fs/fdos/subdir.c | 348 | ||||
-rwxr-xr-x | fs/fdos/vfat.c | 357 |
9 files changed, 1675 insertions, 0 deletions
diff --git a/fs/fdos/Makefile b/fs/fdos/Makefile new file mode 100755 index 0000000..c25e744 --- /dev/null +++ b/fs/fdos/Makefile @@ -0,0 +1,49 @@ +# +# (C) Copyright 2002 +# Stäubli Faverges - <www.staubli.com> +# Pierre AUBERT p.aubert@staubli.com +# +# +# See file CREDITS for list of people who contributed to this +# project. +# +# 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 $(TOPDIR)/config.mk + +LIB = libfdos.a + +AOBJS = +COBJS = fat.o vfat.o dev.o fdos.o fs.o subdir.o +OBJS = $(AOBJS) $(COBJS) + +#CPPFLAGS += + +all: $(LIB) $(AOBJS) + +$(LIB): .depend $(OBJS) + $(AR) crv $@ $(OBJS) + + +######################################################################### + +.depend: Makefile $(AOBJS:.o=.S) $(COBJS:.o=.c) + $(CC) -M $(CFLAGS) $(AOBJS:.o=.S) $(COBJS:.o=.c) > $@ + +sinclude .depend + +######################################################################### diff --git a/fs/fdos/dev.c b/fs/fdos/dev.c new file mode 100755 index 0000000..5dea5cd --- /dev/null +++ b/fs/fdos/dev.c @@ -0,0 +1,195 @@ +/* + * (C) Copyright 2002 + * Stäubli Faverges - <www.staubli.com> + * Pierre AUBERT p.aubert@staubli.com + * + * See file CREDITS for list of people who contributed to this + * project. + * + * 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 <common.h> +#include <config.h> + +#include "dos.h" +#include "fdos.h" + +#if (CONFIG_COMMANDS & CFG_CMD_FDOS) + +#define NB_HEADS 2 +#define NB_TRACKS 80 +#define NB_SECTORS 18 + + +static int lastwhere; + +/*----------------------------------------------------------------------------- + * dev_open -- + *----------------------------------------------------------------------------- + */ +int dev_open (void) +{ + lastwhere = 0; + return (0); +} + +/*----------------------------------------------------------------------------- + * dev_read -- len and where are sectors number + *----------------------------------------------------------------------------- + */ +int dev_read (void *buffer, int where, int len) +{ + PRINTF ("dev_read (len = %d, where = %d)\n", len, where); + + /* Si on ne desire pas lire a la position courante, il faut un seek */ + if (where != lastwhere) { + if (!fdc_fdos_seek (where)) { + PRINTF ("seek error in dev_read"); + lastwhere = -1; + return (-1); + } + } + + if (!fdc_fdos_read (buffer, len)) { + PRINTF ("read error\n"); + lastwhere = -1; + return (-1); + } + lastwhere = where + len; + return (0); +} +/*----------------------------------------------------------------------------- + * check_dev -- verify the diskette format + *----------------------------------------------------------------------------- + */ +int check_dev (BootSector_t *boot, Fs_t *fs) +{ + unsigned int heads, sectors, tracks; + int BootP, Infp0, InfpX, InfTm; + int sect_per_track; + + /* Display Boot header */ + PRINTF ("Jump to boot code 0x%02x 0x%02x 0x%02x\n", + boot -> jump [0], boot -> jump [1], boot -> jump[2]); + PRINTF ("OEM name & version '%*.*s'\n", + BANNER_LG, BANNER_LG, boot -> banner ); + PRINTF ("Bytes per sector hopefully 512 %d\n", + __le16_to_cpu (boot -> secsiz)); + PRINTF ("Cluster size in sectors %d\n", + boot -> clsiz); + PRINTF ("Number of reserved (boot) sectors %d\n", + __le16_to_cpu (boot -> nrsvsect)); + PRINTF ("Number of FAT tables hopefully 2 %d\n", + boot -> nfat); + PRINTF ("Number of directory slots %d\n", + __le16_to_cpu (boot -> dirents)); + PRINTF ("Total sectors on disk %d\n", + __le16_to_cpu (boot -> psect)); + PRINTF ("Media descriptor=first byte of FAT %d\n", + boot -> descr); + PRINTF ("Sectors in FAT %d\n", + __le16_to_cpu (boot -> fatlen)); + PRINTF ("Sectors/track %d\n", + __le16_to_cpu (boot -> nsect)); + PRINTF ("Heads %d\n", + __le16_to_cpu (boot -> nheads)); + PRINTF ("number of hidden sectors %d\n", + __le32_to_cpu (boot -> nhs)); + PRINTF ("big total sectors %d\n", + __le32_to_cpu (boot -> bigsect)); + PRINTF ("physical drive ? %d\n", + boot -> physdrive); + PRINTF ("reserved %d\n", + boot -> reserved); + PRINTF ("dos > 4.0 diskette %d\n", + boot -> dos4); + PRINTF ("serial number %d\n", + __le32_to_cpu (boot -> serial)); + PRINTF ("disk label %*.*s\n", + LABEL_LG, LABEL_LG, boot -> label); + PRINTF ("FAT type %8.8s\n", + boot -> fat_type); + PRINTF ("reserved by 2M %d\n", + boot -> res_2m); + PRINTF ("2M checksum (not used) %d\n", + boot -> CheckSum); + PRINTF ("2MF format version %d\n", + boot -> fmt_2mf); + PRINTF ("1 if write track after format %d\n", + boot -> wt); + PRINTF ("data transfer rate on track 0 %d\n", + boot -> rate_0); + PRINTF ("data transfer rate on track<>0 %d\n", + boot -> rate_any); + PRINTF ("offset to boot program %d\n", + __le16_to_cpu (boot -> BootP)); + PRINTF ("T1: information for track 0 %d\n", + __le16_to_cpu (boot -> Infp0)); + PRINTF ("T2: information for track<>0 %d\n", + __le16_to_cpu (boot -> InfpX)); + PRINTF ("T3: track sectors size table %d\n", + __le16_to_cpu (boot -> InfTm)); + PRINTF ("Format date 0x%04x\n", + __le16_to_cpu (boot -> DateF)); + PRINTF ("Format time 0x%04x\n", + __le16_to_cpu (boot -> TimeF)); + + + /* information is extracted from boot sector */ + heads = __le16_to_cpu (boot -> nheads); + sectors = __le16_to_cpu (boot -> nsect); + fs -> tot_sectors = __le32_to_cpu (boot -> bigsect); + if (__le16_to_cpu (boot -> psect) != 0) { + fs -> tot_sectors = __le16_to_cpu (boot -> psect); + } + + sect_per_track = heads * sectors; + tracks = (fs -> tot_sectors + sect_per_track - 1) / sect_per_track; + + BootP = __le16_to_cpu (boot -> BootP); + Infp0 = __le16_to_cpu (boot -> Infp0); + InfpX = __le16_to_cpu (boot -> InfpX); + InfTm = __le16_to_cpu (boot -> InfTm); + + if (boot -> dos4 == EXTENDED_BOOT && + strncmp( boot->banner,"2M", 2 ) == 0 && + BootP < SZ_STD_SECTOR && + Infp0 < SZ_STD_SECTOR && + InfpX < SZ_STD_SECTOR && + InfTm < SZ_STD_SECTOR && + BootP >= InfTm + 2 && + InfTm >= InfpX && + InfpX >= Infp0 && + Infp0 >= 76 ) { + + return (-1); + } + + if (heads != NB_HEADS || + tracks != NB_TRACKS || + sectors != NB_SECTORS || + __le16_to_cpu (boot -> secsiz) != SZ_STD_SECTOR || + fs -> tot_sectors == 0 || + (fs -> tot_sectors % sectors) != 0) { + return (-1); + } + + return (0); +} + + +#endif diff --git a/fs/fdos/dos.h b/fs/fdos/dos.h new file mode 100755 index 0000000..7b27b01 --- /dev/null +++ b/fs/fdos/dos.h @@ -0,0 +1,175 @@ +/* + * (C) Copyright 2002 + * Stäubli Faverges - <www.staubli.com> + * Pierre AUBERT p.aubert@staubli.com + * + * See file CREDITS for list of people who contributed to this + * project. + * + * 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 + */ + +#ifndef _DOS_H_ +#define _DOS_H_ + +/* Definitions for Dos diskettes */ + +/* General definitions */ +#define SZ_STD_SECTOR 512 /* Standard sector size */ +#define MDIR_SIZE 32 /* Direntry size */ +#define FAT_BITS 12 /* Diskette use 12 bits fat */ + +#define MAX_PATH 128 /* Max size of the MSDOS PATH */ +#define MAX_DIR_SECS 64 /* Taille max d'un repertoire (en */ + /* secteurs) */ +/* Misc. definitions */ +#define DELMARK '\xe5' +#define EXTENDED_BOOT (0x29) +#define MEDIA_STD (0xf0) +#define JUMP_0_1 (0xe9) +#define JUMP_0_2 (0xeb) + +/* Boot size is 256 bytes, but we need to read almost a sector, then + assume bootsize is 512 */ +#define BOOTSIZE 512 + +/* Fat definitions for 12 bits fat */ +#define FAT12_MAX_NB 4086 +#define FAT12_LAST 0x0ff6 +#define FAT12_END 0x0fff + +/* file attributes */ +#define ATTR_READONLY 0x01 +#define ATTR_HIDDEN 0x02 +#define ATTR_SYSTEM 0x04 +#define ATTR_VOLUME 0x08 +#define ATTR_DIRECTORY 0x10 +#define ATTR_ARCHIVE 0x20 +#define ATTR_VSE 0x0f + +/* Name format */ +#define EXTCASE 0x10 +#define BASECASE 0x8 + +/* Definition of the boot sector */ +#define BANNER_LG 8 +#define LABEL_LG 11 + +typedef struct bootsector +{ + unsigned char jump [3]; /* 0 Jump to boot code */ + char banner [BANNER_LG]; /* 3 OEM name & version */ + unsigned short secsiz; /* 11 Bytes per sector hopefully 512 */ + unsigned char clsiz; /* 13 Cluster size in sectors */ + unsigned short nrsvsect; /* 14 Number of reserved (boot) sectors */ + unsigned char nfat; /* 16 Number of FAT tables hopefully 2 */ + unsigned short dirents; /* 17 Number of directory slots */ + unsigned short psect; /* 19 Total sectors on disk */ + unsigned char descr; /* 21 Media descriptor=first byte of FAT */ + unsigned short fatlen; /* 22 Sectors in FAT */ + unsigned short nsect; /* 24 Sectors/track */ + unsigned short nheads; /* 26 Heads */ + unsigned int nhs; /* 28 number of hidden sectors */ + unsigned int bigsect; /* 32 big total sectors */ + unsigned char physdrive; /* 36 physical drive ? */ + unsigned char reserved; /* 37 reserved */ + unsigned char dos4; /* 38 dos > 4.0 diskette */ + unsigned int serial; /* 39 serial number */ + char label [LABEL_LG]; /* 43 disk label */ + char fat_type [8]; /* 54 FAT type */ + unsigned char res_2m; /* 62 reserved by 2M */ + unsigned char CheckSum; /* 63 2M checksum (not used) */ + unsigned char fmt_2mf; /* 64 2MF format version */ + unsigned char wt; /* 65 1 if write track after format */ + unsigned char rate_0; /* 66 data transfer rate on track 0 */ + unsigned char rate_any; /* 67 data transfer rate on track<>0 */ + unsigned short BootP; /* 68 offset to boot program */ + unsigned short Infp0; /* 70 T1: information for track 0 */ + unsigned short InfpX; /* 72 T2: information for track<>0 */ + unsigned short InfTm; /* 74 T3: track sectors size table */ + unsigned short DateF; /* 76 Format date */ + unsigned short TimeF; /* 78 Format time */ + unsigned char junk [BOOTSIZE - 80]; /* 80 remaining data */ +} __attribute__ ((packed)) BootSector_t; + +/* Structure d'une entree de repertoire */ +typedef struct directory { + char name [8]; /* file name */ + char ext [3]; /* file extension */ + unsigned char attr; /* attribute byte */ + unsigned char Case; /* case of short filename */ + unsigned char reserved [9]; /* ?? */ + unsigned char time [2]; /* time stamp */ + unsigned char date [2]; /* date stamp */ + unsigned short start; /* starting cluster number */ + unsigned int size; /* size of the file */ +} __attribute__ ((packed)) Directory_t; + + +#define MAX_VFAT_SUBENTRIES 20 +#define VSE_NAMELEN 13 + +#define VSE1SIZE 5 +#define VSE2SIZE 6 +#define VSE3SIZE 2 + +#define VBUFSIZE ((MAX_VFAT_SUBENTRIES * VSE_NAMELEN) + 1) + +#define MAX_VNAMELEN (255) + +#define VSE_PRESENT 0x01 +#define VSE_LAST 0x40 +#define VSE_MASK 0x1f + +/* Flag used by vfat_lookup */ +#define DO_OPEN 1 +#define ACCEPT_PLAIN 0x20 +#define ACCEPT_DIR 0x10 +#define ACCEPT_LABEL 0x08 +#define SINGLE 2 +#define MATCH_ANY 0x40 + +struct vfat_subentry { + unsigned char id; /* VSE_LAST pour la fin, VSE_MASK */ + /* pour un VSE */ + char text1 [VSE1SIZE * 2]; /* Caracteres encodes sur 16 bits */ + unsigned char attribute; /* 0x0f pour les VFAT */ + unsigned char hash1; /* toujours 0 */ + unsigned char sum; /* Checksum du nom court */ + char text2 [VSE2SIZE * 2]; /* Caracteres encodes sur 16 bits */ + unsigned char sector_l; /* 0 pour les VFAT */ + unsigned char sector_u; /* 0 pour les VFAT */ + char text3 [VSE3SIZE * 2]; /* Caracteres encodes sur 16 bits */ +} __attribute__ ((packed)) ; + +struct vfat_state { + char name [VBUFSIZE]; + int status; /* is now a bit map of 32 bits */ + int subentries; + unsigned char sum; /* no need to remember the sum for each */ + /* entry, it is the same anyways */ +} __attribute__ ((packed)) ; + +/* Conversion macros */ +#define DOS_YEAR(dir) (((dir)->date[1] >> 1) + 1980) +#define DOS_MONTH(dir) (((((dir)->date[1]&0x1) << 3) + ((dir)->date[0] >> 5))) +#define DOS_DAY(dir) ((dir)->date[0] & 0x1f) +#define DOS_HOUR(dir) ((dir)->time[1] >> 3) +#define DOS_MINUTE(dir) (((((dir)->time[1]&0x7) << 3) + ((dir)->time[0] >> 5))) +#define DOS_SEC(dir) (((dir)->time[0] & 0x1f) * 2) + + +#endif diff --git a/fs/fdos/fat.c b/fs/fdos/fat.c new file mode 100755 index 0000000..2ef2371 --- /dev/null +++ b/fs/fdos/fat.c @@ -0,0 +1,142 @@ +/* + * (C) Copyright 2002 + * Stäubli Faverges - <www.staubli.com> + * Pierre AUBERT p.aubert@staubli.com + * + * See file CREDITS for list of people who contributed to this + * project. + * + * 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 <common.h> +#include <config.h> +#include <malloc.h> + +#if (CONFIG_COMMANDS & CFG_CMD_FDOS) + +#include "dos.h" +#include "fdos.h" + + +/*----------------------------------------------------------------------------- + * fat_decode -- + *----------------------------------------------------------------------------- + */ +unsigned int fat_decode (Fs_t *fs, unsigned int num) +{ + unsigned int start = num * 3 / 2; + unsigned char *address = fs -> fat_buf + start; + + if (num < 2 || start + 1 > (fs -> fat_len * SZ_STD_SECTOR)) + return 1; + + if (num & 1) + return ((address [1] & 0xff) << 4) | ((address [0] & 0xf0 ) >> 4); + else + return ((address [1] & 0xf) << 8) | (address [0] & 0xff ); +} +/*----------------------------------------------------------------------------- + * check_fat -- + *----------------------------------------------------------------------------- + */ +static int check_fat (Fs_t *fs) +{ + int i, f; + + /* Cluster verification */ + for (i = 3 ; i < fs -> num_clus; i++){ + f = fat_decode (fs, i); + if (f < FAT12_LAST && f > fs -> num_clus){ + /* Wrong cluster number detected */ + return (-1); + } + } + return (0); +} +/*----------------------------------------------------------------------------- + * read_one_fat -- + *----------------------------------------------------------------------------- + */ +static int read_one_fat (BootSector_t *boot, Fs_t *fs, int nfat) +{ + if (dev_read (fs -> fat_buf, + (fs -> fat_start + nfat * fs -> fat_len), + fs -> fat_len) < 0) { + return (-1); + } + + if (fs -> fat_buf [0] || fs -> fat_buf [1] || fs -> fat_buf [2]) { + if ((fs -> fat_buf [0] != boot -> descr && + (fs -> fat_buf [0] != 0xf9 || boot -> descr != MEDIA_STD)) || + fs -> fat_buf [0] < MEDIA_STD){ + /* Unknown Media */ + return (-1); + } + if (fs -> fat_buf [1] != 0xff || fs -> fat_buf [2] != 0xff){ + /* FAT doesn't start with good values */ + return (-1); + } + } + + if (fs -> num_clus >= FAT12_MAX_NB) { + /* Too much clusters */ + return (-1); + } + + return check_fat (fs); +} +/*----------------------------------------------------------------------------- + * read_fat -- + *----------------------------------------------------------------------------- + */ +int read_fat (BootSector_t *boot, Fs_t *fs) +{ + unsigned int buflen; + int i; + + /* Allocate Fat Buffer */ + buflen = fs -> fat_len * SZ_STD_SECTOR; + if (fs -> fat_buf) { + free (fs -> fat_buf); + } + + if ((fs -> fat_buf = malloc (buflen)) == NULL) { + return (-1); + } + + /* Try to read each Fat */ + for (i = 0; i< fs -> nb_fat; i++){ + if (read_one_fat (boot, fs, i) == 0) { + /* Fat is OK */ + fs -> num_fat = i; + break; + } + } + + if (i == fs -> nb_fat){ + return (-1); + } + + if (fs -> fat_len > (((fs -> num_clus + 2) * + (FAT_BITS / 4) -1 ) / 2 / + SZ_STD_SECTOR + 1)) { + return (-1); + } + return (0); +} + +#endif diff --git a/fs/fdos/fdos.c b/fs/fdos/fdos.c new file mode 100755 index 0000000..a29f43d --- /dev/null +++ b/fs/fdos/fdos.c @@ -0,0 +1,175 @@ +/* + * (C) Copyright 2002 + * Stäubli Faverges - <www.staubli.com> + * Pierre AUBERT p.aubert@staubli.com + * + * See file CREDITS for list of people who contributed to this + * project. + * + * 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 <common.h> +#include <config.h> + +#if (CONFIG_COMMANDS & CFG_CMD_FDOS) +#include <malloc.h> +#include "dos.h" +#include "fdos.h" + + +const char *month [] = {"Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"}; + +Fs_t fs; +File_t file; + +/*----------------------------------------------------------------------------- + * dos_open -- + *----------------------------------------------------------------------------- + */ +int dos_open(char *name) +{ + int lg; + int entry; + char *fname; + + /* We need to suppress the " char around the name */ + if (name [0] == '"') { + name ++; + } + lg = strlen (name); + if (name [lg - 1] == '"') { + name [lg - 1] = '\0'; + } + + /* Open file system */ + if (fs_init (&fs) < 0) { + return -1; + } + + /* Init the file descriptor */ + file.name = name; + file.fs = &fs; + + /* find the subdirectory containing the file */ + if (open_subdir (&file) < 0) { + return (-1); + } + + fname = basename (name); + + /* if we try to open root directory */ + if (*fname == '\0') { + file.file = file.subdir; + return (0); + } + + /* find the file in the subdir */ + entry = 0; + if (vfat_lookup (&file.subdir, + file.fs, + &file.file.dir, + &entry, + 0, + fname, + ACCEPT_DIR | ACCEPT_PLAIN | SINGLE | DO_OPEN, + 0, + &file.file) != 0) { + /* File not found */ + printf ("File not found\n"); + return (-1); + } + + return 0; +} + +/*----------------------------------------------------------------------------- + * dos_read -- + *----------------------------------------------------------------------------- + */ +int dos_read (ulong addr) +{ + int read = 0, nb; + + /* Try to boot a directory ? */ + if (file.file.dir.attr & (ATTR_DIRECTORY | ATTR_VOLUME)) { + printf ("Unable to boot %s !!\n", file.name); + return (-1); + } + while (read < file.file.FileSize) { + PRINTF ("read_file (%ld)\n", (file.file.FileSize - read)); + nb = read_file (&fs, + &file.file, + (char *)addr + read, + read, + (file.file.FileSize - read)); + PRINTF ("read_file -> %d\n", nb); + if (nb < 0) { + printf ("read error\n"); + return (-1); + } + read += nb; + } + return (read); +} +/*----------------------------------------------------------------------------- + * dos_dir -- + *----------------------------------------------------------------------------- + */ +int dos_dir (void) +{ + int entry; + Directory_t dir; + char *name; + + + if ((file.file.dir.attr & ATTR_DIRECTORY) == 0) { + printf ("%s: not a directory !!\n", file.name); + return (1); + } + entry = 0; + if ((name = malloc (MAX_VNAMELEN + 1)) == NULL) { + PRINTF ("Allcation error\n"); + return (1); + } + + while (vfat_lookup (&file.file, + file.fs, + &dir, + &entry, + 0, + NULL, + ACCEPT_DIR | ACCEPT_PLAIN | MATCH_ANY, + name, + NULL) == 0) { + /* Display file info */ + printf ("%3.3s %9d %s %02d %04d %02d:%02d:%02d %s\n", + (dir.attr & ATTR_DIRECTORY) ? "dir" : " ", + __le32_to_cpu (dir.size), + month [DOS_MONTH (&dir) - 1], + DOS_DAY (&dir), + DOS_YEAR (&dir), + DOS_HOUR (&dir), + DOS_MINUTE (&dir), + DOS_SEC (&dir), + name); + + } + free (name); + return (0); +} + +#endif diff --git a/fs/fdos/fdos.h b/fs/fdos/fdos.h new file mode 100755 index 0000000..e28c22f --- /dev/null +++ b/fs/fdos/fdos.h @@ -0,0 +1,116 @@ +/* + * (C) Copyright 2002 + * Stäubli Faverges - <www.staubli.com> + * Pierre AUBERT p.aubert@staubli.com + * + * See file CREDITS for list of people who contributed to this + * project. + * + * 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 + */ + +#ifndef _FDOS_H_ +#define _FDOS_H_ + + +#undef FDOS_DEBUG + +#ifdef FDOS_DEBUG +#define PRINTF(fmt,args...) printf (fmt ,##args) +#else +#define PRINTF(fmt,args...) +#endif + +/* Data structure describing media */ +typedef struct fs +{ + unsigned long tot_sectors; + + int cluster_size; + int num_clus; + + int fat_start; + int fat_len; + int nb_fat; + int num_fat; + + int dir_start; + int dir_len; + + unsigned char *fat_buf; + +} Fs_t; + +/* Data structure describing one file system slot */ +typedef struct slot { + int (*map) (struct fs *fs, + struct slot *file, + int where, + int *len); + unsigned long FileSize; + + unsigned short int FirstAbsCluNr; + unsigned short int PreviousAbsCluNr; + unsigned short int PreviousRelCluNr; + + Directory_t dir; +} Slot_t; + +typedef struct file { + char *name; + int Case; + Fs_t *fs; + Slot_t subdir; + Slot_t file; +} File_t; + + +/* dev.c */ +int dev_read (void *buffer, int where, int len); +int dev_open (void); +int check_dev (BootSector_t *boot, Fs_t *fs); + +/* fat.c */ +unsigned int fat_decode (Fs_t *fs, unsigned int num); +int read_fat (BootSector_t *boot, Fs_t *fs); + +/* vfat.c */ +int vfat_lookup (Slot_t *dir, + Fs_t *fs, + Directory_t *dirent, + int *entry, + int *vfat_start, + char *filename, + int flags, + char *outname, + Slot_t *file); + +/* subdir.c */ +char *basename (char *name); +int open_subdir (File_t *desc); +int open_file (Slot_t *file, Directory_t *dir); +int read_file (Fs_t *fs, + Slot_t *file, + char *buf, + int where, + int len); +void init_subdir (void); + +/* fs.c */ +int fs_init (Fs_t *fs); + + +#endif diff --git a/fs/fdos/fs.c b/fs/fdos/fs.c new file mode 100755 index 0000000..3b9d09e --- /dev/null +++ b/fs/fdos/fs.c @@ -0,0 +1,118 @@ +/* + * (C) Copyright 2002 + * Stäubli Faverges - <www.staubli.com> + * Pierre AUBERT p.aubert@staubli.com + * + * See file CREDITS for list of people who contributed to this + * project. + * + * 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 <common.h> +#include <config.h> +#include <malloc.h> + +#if (CONFIG_COMMANDS & CFG_CMD_FDOS) + +#include "dos.h" +#include "fdos.h" + + +/*----------------------------------------------------------------------------- + * fill_fs -- Read info on file system + *----------------------------------------------------------------------------- + */ +static int fill_fs (BootSector_t *boot, Fs_t *fs) +{ + + fs -> fat_start = __le16_to_cpu (boot -> nrsvsect); + fs -> fat_len = __le16_to_cpu (boot -> fatlen); + fs -> nb_fat = boot -> nfat; + + fs -> dir_start = fs -> fat_start + fs -> nb_fat * fs -> fat_len; + fs -> dir_len = __le16_to_cpu (boot -> dirents) * MDIR_SIZE / SZ_STD_SECTOR; + fs -> cluster_size = boot -> clsiz; + fs -> num_clus = (fs -> tot_sectors - fs -> dir_start - fs -> dir_len) / fs -> cluster_size; + + return (0); +} + +/*----------------------------------------------------------------------------- + * fs_init -- + *----------------------------------------------------------------------------- + */ +int fs_init (Fs_t *fs) +{ + BootSector_t *boot; + + /* Initialize physical device */ + if (dev_open () < 0) { + PRINTF ("Unable to initialize the fdc\n"); + return (-1); + } + init_subdir (); + + /* Allocate space for read the boot sector */ + if ((boot = (BootSector_t *)malloc (sizeof (BootSector_t))) == NULL) { + PRINTF ("Unable to allocate space for boot sector\n"); + return (-1); + } + + /* read boot sector */ + if (dev_read (boot, 0, 1)){ + PRINTF ("Error during boot sector read\n"); + free (boot); + return (-1); + } + + /* we verify it'a a DOS diskette */ + if (boot -> jump [0] != JUMP_0_1 && boot -> jump [0] != JUMP_0_2) { + PRINTF ("Not a DOS diskette\n"); + free (boot); + return (-1); + } + + if (boot -> descr < MEDIA_STD) { + /* We handle only recent medias (type F0) */ + PRINTF ("unrecognized diskette type\n"); + free (boot); + return (-1); + } + + if (check_dev (boot, fs) < 0) { + PRINTF ("Bad diskette\n"); + free (boot); + return (-1); + } + + if (fill_fs (boot, fs) < 0) { + free (boot); + + return (-1); + } + + /* Read FAT */ + if (read_fat (boot, fs) < 0) { + free (boot); + return (-1); + } + + free (boot); + return (0); +} + +#endif diff --git a/fs/fdos/subdir.c b/fs/fdos/subdir.c new file mode 100755 index 0000000..97b2504 --- /dev/null +++ b/fs/fdos/subdir.c @@ -0,0 +1,348 @@ +/* + * (C) Copyright 2002 + * Stäubli Faverges - <www.staubli.com> + * Pierre AUBERT p.aubert@staubli.com + * + * See file CREDITS for list of people who contributed to this + * project. + * + * 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 <common.h> +#include <config.h> +#include <malloc.h> + +#if (CONFIG_COMMANDS & CFG_CMD_FDOS) + +#include "dos.h" +#include "fdos.h" + +static int cache_sect; +static unsigned char cache [SZ_STD_SECTOR]; + + +#define min(x,y) ((x)<(y)?(x):(y)) + +static int descend (Slot_t *parent, + Fs_t *fs, + char *path); + +/*----------------------------------------------------------------------------- + * init_subdir -- + *----------------------------------------------------------------------------- + */ +void init_subdir (void) +{ + cache_sect = -1; +} +/*----------------------------------------------------------------------------- + * basename -- + *----------------------------------------------------------------------------- + */ +char *basename (char *name) +{ + register char *cptr; + + if (!name || !*name) { + return (""); + } + + for (cptr= name; *cptr++; ); + while (--cptr >= name) { + if (*cptr == '/') { + return (cptr + 1); + } + } + return(name); +} +/*----------------------------------------------------------------------------- + * root_map -- + *----------------------------------------------------------------------------- + */ +static int root_map (Fs_t *fs, Slot_t *file, int where, int *len) +{ + *len = min (*len, fs -> dir_len * SZ_STD_SECTOR - where); + if (*len < 0 ) { + *len = 0; + return (-1); + } + return fs -> dir_start * SZ_STD_SECTOR + where; +} +/*----------------------------------------------------------------------------- + * normal_map -- + *----------------------------------------------------------------------------- + */ +static int normal_map (Fs_t *fs, Slot_t *file, int where, int *len) +{ + int offset; + int NrClu; + unsigned short RelCluNr; + unsigned short CurCluNr; + unsigned short NewCluNr; + unsigned short AbsCluNr; + int clus_size; + + clus_size = fs -> cluster_size * SZ_STD_SECTOR; + offset = where % clus_size; + + *len = min (*len, file -> FileSize - where); + + if (*len < 0 ) { + *len = 0; + return (0); + } + + if (file -> FirstAbsCluNr < 2){ + *len = 0; + return (0); + } + + RelCluNr = where / clus_size; + + if (RelCluNr >= file -> PreviousRelCluNr){ + CurCluNr = file -> PreviousRelCluNr; + AbsCluNr = file -> PreviousAbsCluNr; + } else { + CurCluNr = 0; + AbsCluNr = file -> FirstAbsCluNr; + } + + + NrClu = (offset + *len - 1) / clus_size; + while (CurCluNr <= RelCluNr + NrClu) { + if (CurCluNr == RelCluNr){ + /* we have reached the beginning of our zone. Save + * coordinates */ + file -> PreviousRelCluNr = RelCluNr; + file -> PreviousAbsCluNr = AbsCluNr; + } + NewCluNr = fat_decode (fs, AbsCluNr); + if (NewCluNr == 1 || NewCluNr == 0) { + PRINTF("Fat problem while decoding %d %x\n", + AbsCluNr, NewCluNr); + return (-1); + } + if (CurCluNr == RelCluNr + NrClu) { + break; + } + + if (CurCluNr < RelCluNr && NewCluNr == FAT12_END) { + *len = 0; + return 0; + } + + if (CurCluNr >= RelCluNr && NewCluNr != AbsCluNr + 1) + break; + CurCluNr++; + AbsCluNr = NewCluNr; + } + + *len = min (*len, (1 + CurCluNr - RelCluNr) * clus_size - offset); + + return (((file -> PreviousAbsCluNr - 2) * fs -> cluster_size + + fs -> dir_start + fs -> dir_len) * + SZ_STD_SECTOR + offset); +} +/*----------------------------------------------------------------------------- + * open_subdir -- open the subdir containing the file + *----------------------------------------------------------------------------- + */ +int open_subdir (File_t *desc) +{ + char *pathname; + char *tmp, *s, *path; + char terminator; + + if ((pathname = (char *)malloc (MAX_PATH)) == NULL) { + return (-1); + } + + strcpy (pathname, desc -> name); + + /* Suppress file name */ + tmp = basename (pathname); + *tmp = '\0'; + + /* root directory init */ + desc -> subdir.FirstAbsCluNr = 0; + desc -> subdir.FileSize = -1; + desc -> subdir.map = root_map; + desc -> subdir.dir.attr = ATTR_DIRECTORY; + + tmp = pathname; + for (s = tmp; ; ++s) { + if (*s == '/' || *s == '\0') { + path = tmp; + terminator = *s; + *s = '\0'; + if (s != tmp && strcmp (path,".")) { + if (descend (&desc -> subdir, desc -> fs, path) < 0) { + free (pathname); + return (-1); + } + } + if (terminator == 0) { + break; + } + tmp = s + 1; + } + } + free (pathname); + return (0); +} +/*----------------------------------------------------------------------------- + * descend -- + *----------------------------------------------------------------------------- + */ +static int descend (Slot_t *parent, + Fs_t *fs, + char *path) +{ + int entry; + Slot_t SubDir; + + if(path[0] == '\0' || strcmp (path, ".") == 0) { + return (0); + } + + + entry = 0; + if (vfat_lookup (parent, + fs, + &(SubDir.dir), + &entry, + 0, + path, + ACCEPT_DIR | SINGLE | DO_OPEN, + 0, + &SubDir) == 0) { + *parent = SubDir; + return (0); + } + + if (strcmp(path, "..") == 0) { + parent -> FileSize = -1; + parent -> FirstAbsCluNr = 0; + parent -> map = root_map; + return (0); + } + return (-1); +} +/*----------------------------------------------------------------------------- + * open_file -- + *----------------------------------------------------------------------------- + */ +int open_file (Slot_t *file, Directory_t *dir) +{ + int first; + unsigned long size; + + first = __le16_to_cpu (dir -> start); + + if(first == 0 && + (dir -> attr & ATTR_DIRECTORY) != 0) { + file -> FirstAbsCluNr = 0; + file -> FileSize = -1; + file -> map = root_map; + return (0); + } + + if ((dir -> attr & ATTR_DIRECTORY) != 0) { + size = (1UL << 31) - 1; + } + else { + size = __le32_to_cpu (dir -> size); + } + + file -> map = normal_map; + file -> FirstAbsCluNr = first; + file -> PreviousRelCluNr = 0xffff; + file -> FileSize = size; + return (0); +} +/*----------------------------------------------------------------------------- + * read_file -- + *----------------------------------------------------------------------------- + */ +int read_file (Fs_t *fs, + Slot_t *file, + char *buf, + int where, + int len) +{ + int pos; + int read, nb, sect, offset; + + pos = file -> map (fs, file, where, &len); + if (pos < 0) { + return -1; + } + if (len == 0) { + return (0); + } + + /* Compute sector number */ + sect = pos / SZ_STD_SECTOR; + offset = pos % SZ_STD_SECTOR; + read = 0; + + if (offset) { + /* Read doesn't start at the sector beginning. We need to use our */ + /* cache */ + if (sect != cache_sect) { + if (dev_read (cache, sect, 1) < 0) { + return (-1); + } + cache_sect = sect; + } + nb = min (len, SZ_STD_SECTOR - offset); + + memcpy (buf, cache + offset, nb); + read += nb; + len -= nb; + sect += 1; + } + + if (len > SZ_STD_SECTOR) { + nb = (len - 1) / SZ_STD_SECTOR; + if (dev_read (buf + read, sect, nb) < 0) { + return ((read) ? read : -1); + } + /* update sector position */ + sect += nb; + + /* Update byte position */ + nb *= SZ_STD_SECTOR; + read += nb; + len -= nb; + } + + if (len) { + if (sect != cache_sect) { + if (dev_read (cache, sect, 1) < 0) { + return ((read) ? read : -1); + cache_sect = -1; + } + cache_sect = sect; + } + + memcpy (buf + read, cache, len); + read += len; + } + return (read); +} +#endif diff --git a/fs/fdos/vfat.c b/fs/fdos/vfat.c new file mode 100755 index 0000000..46a464b --- /dev/null +++ b/fs/fdos/vfat.c @@ -0,0 +1,357 @@ +/* + * (C) Copyright 2002 + * Stäubli Faverges - <www.staubli.com> + * Pierre AUBERT p.aubert@staubli.com + * + * See file CREDITS for list of people who contributed to this + * project. + * + * 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 <common.h> +#include <config.h> + +#if (CONFIG_COMMANDS & CFG_CMD_FDOS) +#include <linux/ctype.h> + +#include "dos.h" +#include "fdos.h" + +static int dir_read (Fs_t *fs, + Slot_t *dir, + Directory_t *dirent, + int num, + struct vfat_state *v); + +static int unicode_read (char *in, char *out, int num); +static int match (const char *s, const char *p); +static unsigned char sum_shortname (char *name); +static int check_vfat (struct vfat_state *v, Directory_t *dir); +static char *conv_name (char *name, char *ext, char Case, char *ans); + + +/*----------------------------------------------------------------------------- + * clear_vfat -- + *----------------------------------------------------------------------------- + */ +static void clear_vfat (struct vfat_state *v) +{ + v -> subentries = 0; + v -> status = 0; +} + +/*----------------------------------------------------------------------------- + * vfat_lookup -- + *----------------------------------------------------------------------------- + */ +int vfat_lookup (Slot_t *dir, + Fs_t *fs, + Directory_t *dirent, + int *entry, + int *vfat_start, + char *filename, + int flags, + char *outname, + Slot_t *file) +{ + int found; + struct vfat_state vfat; + char newfile [VSE_NAMELEN]; + int vfat_present = 0; + + if (*entry == -1) { + return -1; + } + + found = 0; + clear_vfat (&vfat); + while (1) { + if (dir_read (fs, dir, dirent, *entry, &vfat) < 0) { + if (vfat_start) { + *vfat_start = *entry; + } + break; + } + (*entry)++; + + /* Empty slot */ + if (dirent -> name[0] == '\0'){ + if (vfat_start == 0) { + break; + } + continue; + } + + if (dirent -> attr == ATTR_VSE) { + /* VSE entry, continue */ + continue; + } + if ( (dirent -> name [0] == DELMARK) || + ((dirent -> attr & ATTR_DIRECTORY) != 0 && + (flags & ACCEPT_DIR) == 0) || + ((dirent -> attr & ATTR_VOLUME) != 0 && + (flags & ACCEPT_LABEL) == 0) || + (((dirent -> attr & (ATTR_DIRECTORY | ATTR_VOLUME)) == 0) && + (flags & ACCEPT_PLAIN) == 0)) { + clear_vfat (&vfat); + continue; + } + + vfat_present = check_vfat (&vfat, dirent); + if (vfat_start) { + *vfat_start = *entry - 1; + if (vfat_present) { + *vfat_start -= vfat.subentries; + } + } + + if (dirent -> attr & ATTR_VOLUME) { + strncpy (newfile, dirent -> name, 8); + newfile [8] = '\0'; + strncat (newfile, dirent -> ext, 3); + newfile [11] = '\0'; + } + else { + conv_name (dirent -> name, dirent -> ext, dirent -> Case, newfile); + } + + if (flags & MATCH_ANY) { + found = 1; + break; + } + + if ((vfat_present && match (vfat.name, filename)) || + (match (newfile, filename))) { + found = 1; + break; + } + clear_vfat (&vfat); + } + + if (found) { + if ((flags & DO_OPEN) && file) { + if (open_file (file, dirent) < 0) { + return (-1); + } + } + if (outname) { + if (vfat_present) { + strcpy (outname, vfat.name); + } + else { + strcpy (outname, newfile); + } + } + return (0); /* File found */ + } else { + *entry = -1; + return -1; /* File not found */ + } +} + +/*----------------------------------------------------------------------------- + * dir_read -- Read one directory entry + *----------------------------------------------------------------------------- + */ +static int dir_read (Fs_t *fs, + Slot_t *dir, + Directory_t *dirent, + int num, + struct vfat_state *v) +{ + + /* read the directory entry */ + if (read_file (fs, + dir, + (char *)dirent, + num * MDIR_SIZE, + MDIR_SIZE) != MDIR_SIZE) { + return (-1); + } + + if (v && (dirent -> attr == ATTR_VSE)) { + struct vfat_subentry *vse; + unsigned char id, last_flag; + char *c; + + vse = (struct vfat_subentry *) dirent; + id = vse -> id & VSE_MASK; + last_flag = (vse -> id & VSE_LAST); + if (id > MAX_VFAT_SUBENTRIES) { + /* Invalid VSE entry */ + return (-1); + } + + + /* Decode VSE */ + if(v -> sum != vse -> sum) { + clear_vfat (v); + v -> sum = vse -> sum; + } + + + v -> status |= 1 << (id - 1); + if (last_flag) { + v -> subentries = id; + } + + c = &(v -> name [VSE_NAMELEN * (id - 1)]); + c += unicode_read (vse->text1, c, VSE1SIZE); + c += unicode_read (vse->text2, c, VSE2SIZE); + c += unicode_read (vse->text3, c, VSE3SIZE); + + if (last_flag) { + *c = '\0'; /* Null terminate long name */ + } + + } + return (0); +} + +/*----------------------------------------------------------------------------- + * unicode_read -- + *----------------------------------------------------------------------------- + */ +static int unicode_read (char *in, char *out, int num) +{ + int j; + + for (j = 0; j < num; ++j) { + if (in [1]) + *out = '_'; + else + *out = in [0]; + out ++; + in += 2; + } + return num; +} + +/*----------------------------------------------------------------------------- + * match -- + *----------------------------------------------------------------------------- + */ +static int match (const char *s, const char *p) +{ + + for (; *p != '\0'; ) { + if (toupper (*s) != toupper (*p)) { + return (0); + } + p++; + s++; + } + + if (*s != '\0') { + return (0); + } + else { + return (1); + } +} +/*----------------------------------------------------------------------------- + * sum_shortname -- + *----------------------------------------------------------------------------- + */ +static unsigned char sum_shortname (char *name) +{ + unsigned char sum; + int j; + + for (j = sum = 0; j < 11; ++j) { + sum = ((sum & 1) ? 0x80 : 0) + (sum >> 1) + + (name [j] ? name [j] : ' '); + } + return (sum); +} +/*----------------------------------------------------------------------------- + * check_vfat -- + * Return 1 if long name is valid, 0 else + *----------------------------------------------------------------------------- + */ +static int check_vfat (struct vfat_state *v, Directory_t *dir) +{ + char name[12]; + + if (v -> subentries == 0) { + return 0; + } + + strncpy (name, dir -> name, 8); + strncpy (name + 8, dir -> ext, 3); + name [11] = '\0'; + + if (v -> sum != sum_shortname (name)) { + return 0; + } + + if( (v -> status & ((1 << v -> subentries) - 1)) != + (1 << v -> subentries) - 1) { + return 0; + } + v->name [VSE_NAMELEN * v -> subentries] = 0; + + return 1; +} +/*----------------------------------------------------------------------------- + * conv_name -- + *----------------------------------------------------------------------------- + */ +static char *conv_name (char *name, char *ext, char Case, char *ans) +{ + char tname [9], text [4]; + int i; + + i = 0; + while (i < 8 && name [i] != ' ' && name [i] != '\0') { + tname [i] = name [i]; + i++; + } + tname [i] = '\0'; + + if (Case & BASECASE) { + for (i = 0; i < 8 && tname [i]; i++) { + tname [i] = tolower (tname [i]); + } + } + + i = 0; + while (i < 3 && ext [i] != ' ' && ext [i] != '\0') { + text [i] = ext [i]; + i++; + } + text [i] = '\0'; + + if (Case & EXTCASE){ + for (i = 0; i < 3 && text [i]; i++) { + text [i] = tolower (text [i]); + } + } + + if (*text) { + strcpy (ans, tname); + strcat (ans, "."); + strcat (ans, text); + } + else { + strcpy(ans, tname); + } + return (ans); +} + + +#endif |