From 20503b61a7f73fad8ee97219f7a1e74de3a8a2ac Mon Sep 17 00:00:00 2001 From: Michael Brandt Date: Tue, 20 Jul 2010 15:08:22 +0200 Subject: Read/write VFAT support from Rockbox-3.3 FAT stack Add read/write VFAT support from Rockbox-3.3 FAT stack. It should be also applicable to the unmodified 2009.11 U-Boot release. Note that is was taken as is from Rockbox and from a older U-Boot Rockbox patch. "checkpatch" shows very many coding style errors and warnings, but it is tedious work to clean this up. To make this patch work an additional mmc_block_write() board support routine and the errno variable are needed. Furthermore following defines in the board config header file: #define CONFIG_ROCKBOX_FAT 1 #define CONFIG_U_BOOT 1 #define CONFIG_SUPPORT_VFAT 1 #define CONFIG_CMD_TREE_FAT This will be added in a follow-up patch. This patch is based on the patch from Etienne Carriere for the U671x U-Boot: This commit adds FAT write support to u-boot native read-only FAT code. Commit initially applied on u-boot-v2009.01 (SHA1: 72d15e705bc3983884105cb7755c7ba80e74a0a5) Based on FAT stack dumped from Rockbox package v3.1 (www.rockbox.org). Based on initial Rockbox FAT stack integration in u-boot by Keith Outwater (outwater@comcast.net). Current porting is aligned with Rockbox v3.3 FAT stack. Enable upon config switches: CONFIG_CMD_FAT CONFIG_ROCKBOX_FAT CONFIG_CMD_TREE_FAT (recommended) CONFIG_SUPPORT_VFAT (recommended) C code APIs (from U-boot native FAT support): int fat_register_device(block_dev_desc_t *dev_desc, int part_no); long file_fat_read(const char *path, void *buf, unsigned long maxsize); int file_fat_ls(const char *dirname); int file_fat_detectfs(void); C code APIs (added by Rockbox FAT support): long file_fat_write(const char *path, void *buf, unsigned long maxsize); int file_fat_rm(const char *path); int file_fat_rmdir(const char *path); int file_fat_mkdir(const char *path); int file_fat_cd(const char *path); int file_fat_pwd(void); int file_fat_mv(const char *oldpath, const char *newpath); unsigned int rockbox_fat_free(unsigned long size_kbyte); unsigned int rockbox_fat_size(void); Use "help fat" from u-boot console to see available commands. ST-Ericsson ID: WP264488 Change-Id: I9afc29ecb80f9152bd8534bbf11e47e54cfad796 Signed-off-by: Michael Brandt Reviewed-on: http://gerrit.lud.stericsson.com/gerrit/3009 --- common/Makefile | 1 + common/cmd_fat.c | 592 ++++++++++++++++++++++++++++++++++++++++++++++++++++++- common/unicode.c | 441 +++++++++++++++++++++++++++++++++++++++++ 3 files changed, 1027 insertions(+), 7 deletions(-) create mode 100644 common/unicode.c (limited to 'common') diff --git a/common/Makefile b/common/Makefile index de594b4a1..c82f08122 100755 --- a/common/Makefile +++ b/common/Makefile @@ -40,6 +40,7 @@ COBJS-y += s_record.o COBJS-$(CONFIG_SERIAL_MULTI) += serial.o COBJS-y += stdio.o COBJS-y += xyzModem.o +COBJS-y += unicode.o # core command COBJS-y += cmd_boot.o diff --git a/common/cmd_fat.c b/common/cmd_fat.c index f3089a296..4b40826ec 100644 --- a/common/cmd_fat.c +++ b/common/cmd_fat.c @@ -2,6 +2,10 @@ * (C) Copyright 2002 * Richard Jones, rjones@nexus-tech.net * + * Keith Outwater (outwater4@comcast.net) - add additional + * FAT commands for use with rockbox (www.rockbox.org) FAT + * filesystem driver. + * * See file CREDITS for list of people who contributed to this * project. * @@ -45,7 +49,8 @@ int do_fat_fsload (cmd_tbl_t *cmdtp, int flag, int argc, char *argv[]) char *ep; if (argc < 5) { - printf ("usage: fatload [bytes]\n"); + printf ("usage: fatload " + " [bytes]\n"); return 1; } dev = (int)simple_strtoul (argv[2], &ep, 16); @@ -62,7 +67,8 @@ int do_fat_fsload (cmd_tbl_t *cmdtp, int flag, int argc, char *argv[]) part = (int)simple_strtoul(++ep, NULL, 16); } if (fat_register_device(dev_desc,part)!=0) { - printf ("\n** Unable to use %s %d:%d for fatload **\n",argv[1],dev,part); + printf ("\n** Unable to use %s %d:%d for fatload **\n", + argv[1],dev,part); return 1; } offset = simple_strtoul (argv[3], NULL, 16); @@ -73,26 +79,511 @@ int do_fat_fsload (cmd_tbl_t *cmdtp, int flag, int argc, char *argv[]) size = file_fat_read (argv[4], (unsigned char *) offset, count); if(size==-1) { - printf("\n** Unable to read \"%s\" from %s %d:%d **\n",argv[4],argv[1],dev,part); + printf("\n** Unable to read \"%s\" from %s %d:%d **\n", + argv[4],argv[1],dev,part); return 1; } - printf ("\n%ld bytes read\n", size); + printf ("\n%ld bytes read\n", (ulong)size); - sprintf(buf, "%lX", size); + sprintf(buf, "0x%lX", size); setenv("filesize", buf); return 0; } +#ifndef CONFIG_CMD_TREE_FAT +#ifdef CONFIG_ROCKBOX_FAT U_BOOT_CMD( fatload, 6, 0, do_fat_fsload, "load binary file from a dos filesystem", - " [bytes]\n" + " [bytes]\n" " - load binary file 'filename' from 'dev' on 'interface'\n" " to address 'addr' from dos filesystem" ); +#else +U_BOOT_CMD( + fatload, 6, 0, do_fat_fsload, + "load binary file from a dos FAT16/32 filesystem\n", + " [bytes]\n" + " - load binary file 'filename' from 'dev' on 'interface'\n" + " to address 'addr' from dos FAT16/32 filesystem\n" +); +#endif +#endif /* #ifndef CONFIG_CMD_TREE_FAT */ + +#ifdef CONFIG_ROCKBOX_FAT +int +do_fat_fswrite (cmd_tbl_t *cmdtp, int flag, int argc, char *argv[]) +{ + long size; + unsigned long offset; + unsigned long count; + char buf[12]; + block_dev_desc_t *dev_desc=NULL; + int dev=0; + int part=1; + char *ep; + + if (argc < 6) { + printf ("usage: fatwrite " + " \n"); + return 1; + } + + dev = (int)simple_strtoul (argv[2], &ep, 16); + dev_desc=get_dev(argv[1],dev); + if (dev_desc==NULL) { + puts ("\n** Invalid device **\n"); + return 1; + } + + if (*ep) { + if (*ep != ':') { + puts ("\n** Invalid device, use `dev[:part]' **\n"); + return 1; + } + part = (int)simple_strtoul(++ep, NULL, 16); + } + + if (fat_register_device(dev_desc,part)!=0) { + printf ("\n** Unable to use %s %d:%d for fatwrite **\n", + argv[1],dev,part); + return 1; + } + + offset = simple_strtoul (argv[3], NULL, 16); + count = simple_strtoul (argv[5], NULL, 16); + size = file_fat_write(argv[4], (unsigned char *)offset, count); + + if (size <= 0) { + printf("\n** Unable to write\"%s\" to %s %d:%d **\n", + argv[4],argv[1],dev,part); + return 1; + } + + printf ("\n%ld bytes written\n", size); + sprintf(buf, "0x%lX", size); + setenv("filesize", buf); + return 0; +} + +#ifndef CONFIG_CMD_TREE_FAT +U_BOOT_CMD( + fatwrite, 6, 0, do_fat_fswrite, + "store a binary file to a dos filesystem\n", + " \n" + " - store binary file 'filename' to 'dev' on 'interface'\n" + " containing a dos filesystem from address 'addr'\n" + " with length 'bytes'\n" +); +#endif + +int +do_fat_fsrm (cmd_tbl_t *cmdtp, int flag, int argc, char *argv[]) +{ + char buf[256]; + block_dev_desc_t *dev_desc=NULL; + int dev=0; + int part=1; + char *ep; + + if (argc < 4) { + printf ("usage: fatrm \n"); + return 1; + } + + dev = (int)simple_strtoul (argv[2], &ep, 16); + dev_desc=get_dev(argv[1],dev); + if (dev_desc==NULL) { + puts ("\n** Invalid device **\n"); + return 1; + } + + if (*ep) { + if (*ep != ':') { + puts ("\n** Invalid device, use `dev[:part]' **\n"); + return 1; + } + part = (int)simple_strtoul(++ep, NULL, 16); + } + + if (fat_register_device(dev_desc,part)!=0) { + printf ("\n** Unable to use %s %d:%d for fatrm **\n", + argv[1],dev,part); + return 1; + } + + if (file_fat_rm(argv[3]) < 0) { + printf("\n** Unable to remove \"%s\" **\n", argv[3]); + return 1; + } + + strncpy(buf, argv[3], sizeof(buf)); + setenv("rm_filename", buf); + return 0; +} + +#ifndef CONFIG_CMD_TREE_FAT +U_BOOT_CMD( + fatrm, 4, 0, do_fat_fsrm, + "remove a file in a FAT16/32 filesystem\n", + " \n" + " - remove 'filename' in the FAT16/32 filesytem\n" + " on 'dev' on 'interface'\n" +); +#endif + +int +do_fat_fsmkdir (cmd_tbl_t *cmdtp, int flag, int argc, char *argv[]) +{ + char buf[256]; + block_dev_desc_t *dev_desc=NULL; + int dev=0; + int part=1; + char *ep; + + if (argc < 4) { + printf ("usage: fatmkdir \n"); + return 1; + } + + dev = (int)simple_strtoul (argv[2], &ep, 16); + dev_desc=get_dev(argv[1],dev); + if (dev_desc==NULL) { + puts ("\n** Invalid device **\n"); + return 1; + } + + if (*ep) { + if (*ep != ':') { + puts ("\n** Invalid device, use `dev[:part]' **\n"); + return 1; + } + part = (int)simple_strtoul(++ep, NULL, 16); + } + + if (fat_register_device(dev_desc,part)!=0) { + printf ("\n** Unable to use %s %d:%d for fatmkdir **\n", + argv[1],dev,part); + return 1; + } + + if (file_fat_mkdir(argv[3]) < 0) { + printf("\n** Unable to make dir \"%s\" **\n", argv[3]); + return 1; + } + + strncpy(buf, argv[3], sizeof(buf)); + setenv("mkdir_dirname", buf); + return 0; +} + +#ifndef CONFIG_CMD_TREE_FAT +U_BOOT_CMD( + fatmkdir, 4, 0, do_fat_fsmkdir, + "make a directory in a FAT16/32 filesystem\n", + " \n" + " - make 'dirname' in the FAT16/32 filesytem on 'dev'\n" + " on 'interface'\n" +); +#endif + +int +do_fat_fsrmdir (cmd_tbl_t *cmdtp, int flag, int argc, char *argv[]) +{ + char buf[256]; + block_dev_desc_t *dev_desc=NULL; + int dev=0; + int part=1; + char *ep; + + if (argc < 4) { + printf ("usage: fatrmdir \n"); + return 1; + } + + dev = (int)simple_strtoul (argv[2], &ep, 16); + dev_desc=get_dev(argv[1],dev); + if (dev_desc==NULL) { + puts ("\n** Invalid device **\n"); + return 1; + } + + if (*ep) { + if (*ep != ':') { + puts ("\n** Invalid device, use `dev[:part]' **\n"); + return 1; + } + part = (int)simple_strtoul(++ep, NULL, 16); + } + + if (fat_register_device(dev_desc,part)!=0) { + printf ("\n** Unable to use %s %d:%d for fatrmdir **\n", + argv[1],dev,part); + return 1; + } + + if (file_fat_rmdir(argv[3]) < 0) { + printf("\n** Unable to remove dir \"%s\" **\n", argv[3]); + return 1; + } + + strncpy(buf, argv[3], sizeof(buf)); + setenv("rm_dirname", buf); + return 0; +} + +#ifndef CONFIG_CMD_TREE_FAT +U_BOOT_CMD( + fatrmdir, 4, 0, do_fat_fsrmdir, + "remove an empty directory in a FAT16/32 filesystem\n", + " \n" + " - remove 'dirname' in the FAT16/32 filesytem on 'dev'\n" + " on 'interface'\n" +); +#endif + +int +do_fat_fscd (cmd_tbl_t *cmdtp, int flag, int argc, char *argv[]) +{ + char buf[256]; + block_dev_desc_t *dev_desc=NULL; + int dev=0; + int part=1; + char *ep; + + if (argc < 4) { + printf ("usage: fatcd \n"); + return 1; + } + + dev = (int)simple_strtoul (argv[2], &ep, 16); + dev_desc=get_dev(argv[1],dev); + if (dev_desc==NULL) { + puts ("\n** Invalid device **\n"); + return 1; + } + + if (*ep) { + if (*ep != ':') { + puts ("\n** Invalid device, use `dev[:part]' **\n"); + return 1; + } + part = (int)simple_strtoul(++ep, NULL, 16); + } + + if (fat_register_device(dev_desc,part)!=0) { + printf ("\n** Unable to use %s %d:%d for fatcd **\n", + argv[1],dev,part); + return 1; + } + + if (file_fat_cd(argv[3]) < 0) { + printf("\n** Unable to change to dir \"%s\" **\n", argv[3]); + return 1; + } + + strncpy(buf, argv[3], sizeof(buf)); + setenv("cwd", buf); + return 0; +} + +#ifndef CONFIG_CMD_TREE_FAT +U_BOOT_CMD( + fatcd, 4, 0, do_fat_fscd, + "change to the specified dir in a FAT16/32 filesystem\n", + " \n" + " - change to 'dirname' in the FAT16/32 filesytem on 'dev'\n" + " on 'interface'\n" +); +#endif + +int +do_fat_fspwd (cmd_tbl_t *cmdtp, int flag, int argc, char *argv[]) +{ + block_dev_desc_t *dev_desc=NULL; + int dev=0; + int part=1; + char *ep; + + if (argc < 3) { + printf ("usage: fatpwd \n"); + return 1; + } + + dev = (int)simple_strtoul (argv[2], &ep, 16); + dev_desc=get_dev(argv[1],dev); + if (dev_desc==NULL) { + puts ("\n** Invalid device **\n"); + return 1; + } + + if (*ep) { + if (*ep != ':') { + puts ("\n** Invalid device, use `dev[:part]' **\n"); + return 1; + } + part = (int)simple_strtoul(++ep, NULL, 16); + } + + if (fat_register_device(dev_desc,part)!=0) { + printf ("\n** Unable to use %s %d:%d for fatpwd **\n", + argv[1],dev,part); + return 1; + } + + file_fat_pwd(); + return 0; +} + +#ifndef CONFIG_CMD_TREE_FAT +U_BOOT_CMD( + fatpwd, 3, 0, do_fat_fspwd, + "print the current working dir in a FAT16/32 filesystem\n", + " \n" + " - print the CWD in the FAT16/32 filesytem on 'dev'\n" + " on 'interface'\n" +); +#endif + +int +do_fat_fsmv (cmd_tbl_t *cmdtp, int flag, int argc, char *argv[]) +{ + char buf[256]; + block_dev_desc_t *dev_desc=NULL; + int dev=0; + int part=1; + char *ep; + + if (argc < 5) { + printf ("usage: fatmv \n"); + return 1; + } + + dev = (int)simple_strtoul (argv[2], &ep, 16); + dev_desc=get_dev(argv[1],dev); + if (dev_desc==NULL) { + puts ("\n** Invalid device **\n"); + return 1; + } + + if (*ep) { + if (*ep != ':') { + puts ("\n** Invalid device, use `dev[:part]' **\n"); + return 1; + } + part = (int)simple_strtoul(++ep, NULL, 16); + } + + if (fat_register_device(dev_desc,part)!=0) { + printf ("\n** Unable to use %s %d:%d for fatmv **\n", + argv[1],dev,part); + return 1; + } + + if (file_fat_mv(argv[3], argv[4]) < 0) { + printf("\n** Unable to change \"%s\" to \"%s\" **\n", + argv[3], argv[4]); + return 1; + } + + strncpy(buf, argv[3], sizeof(buf)); + setenv("filename", buf); + return 0; +} + +#ifndef CONFIG_CMD_TREE_FAT +U_BOOT_CMD( + fatmv, 5, 0, do_fat_fsmv, + "move a file in a FAT16/32 filesystem\n", + " \n" + " - change to in the FAT16/32 filesytem\n" + " on 'dev' on 'interface'\n" +); +#endif + +int +do_fat_free (cmd_tbl_t *cmdtp, int flag, int argc, char *argv[]) +{ + block_dev_desc_t *dev_desc=NULL; + int dev=0; + int part=1; + char *ep; + unsigned int size; + + if (argc < 3) { + printf ("usage: fatfree []\n"); + return 1; + } + + dev = (int)simple_strtoul (argv[2], &ep, 16); + dev_desc=get_dev(argv[1],dev); + if (dev_desc==NULL) { + puts ("\n** Invalid device **\n"); + return 1; + } + + if (*ep) { + if (*ep != ':') { + puts ("\n** Invalid device, use `dev[:part]' **\n"); + return 1; + } + part = (int)simple_strtoul(++ep, NULL, 16); + } + + if (fat_register_device(dev_desc,part)!=0) { + printf ("\n** Unable to use %s %d:%d for fatmv **\n", + argv[1],dev,part); + return 1; + } + + if(argc>=4) { + size = simple_strtoul(argv[3], NULL, 10); + printf("Scanning FAT partition for "); + if (size>0x00100000) printf("%d.%02dMB", (int)(size>>20), (int)(size&0x000FFFFF)>>10); + else if (size>0x00000400) printf("%d.%02dkB", (int)(size>>10), (int)(size&0x000003FF)); + else printf("%d Byte", (int)size); + printf(" of free space.\n"); + if (rockbox_fat_free(size>>10)) { + printf("Enougth free space found.\n"); + return 0; + } else { + printf("Not enougth free space found in partition.\n"); + return 1; + } + } + { unsigned long size; + printf("Scanning full FAT partition for free clusters...\n"); + size = rockbox_fat_size(); + printf("FAT filesystem: total size = "); + if (size>=0x00100000) printf("%d.%02dGB", (int)(size>>20), (int)(size&0x000FFFFF)>>10); + else if (size>=0x00000400) printf("%d.%02dMB", (int)(size>>10), (int)(size&0x000003FF)); + else printf("%dkB", (int)size); + printf(" / free size = "); + size = rockbox_fat_free(0); + if (size>=0x00100000) printf("%d.%02dGB", (int)(size>>20), (int)(size&0x000FFFFF)>>10); + else if (size>=0x00000400) printf("%d.%02dMB", (int)(size>>10), (int)(size&0x000003FF)); + else printf("%dkB", (int)size); + putc('\n'); + } + return 0; +} + +#ifndef CONFIG_CMD_TREE_FAT +U_BOOT_CMD( + fatfree, 4, 0, do_fat_free, + "print free space\n", + " [size]\n" + " - print free sapce available in filesystem.\n" + " If not specified, full partition is scanned.\n" + " If is specified, return true if enougth free space found.\n" +); +#endif + + +#endif /* #ifdef CONFIG_ROCKBOX_FAT */ int do_fat_ls (cmd_tbl_t *cmdtp, int flag, int argc, char *argv[]) { @@ -121,7 +612,8 @@ int do_fat_ls (cmd_tbl_t *cmdtp, int flag, int argc, char *argv[]) part = (int)simple_strtoul(++ep, NULL, 16); } if (fat_register_device(dev_desc,part)!=0) { - printf ("\n** Unable to use %s %d:%d for fatls **\n",argv[1],dev,part); + printf ("\n** Unable to use %s %d:%d for fatls **\n", + argv[1],dev,part); return 1; } if (argc == 4) @@ -134,12 +626,14 @@ int do_fat_ls (cmd_tbl_t *cmdtp, int flag, int argc, char *argv[]) return (ret); } +#ifndef CONFIG_CMD_TREE_FAT U_BOOT_CMD( fatls, 4, 1, do_fat_ls, "list files in a directory (default /)", " [directory]\n" " - list files from 'dev' on 'interface' in a 'directory'" ); +#endif int do_fat_fsinfo (cmd_tbl_t *cmdtp, int flag, int argc, char *argv[]) { @@ -172,12 +666,14 @@ int do_fat_fsinfo (cmd_tbl_t *cmdtp, int flag, int argc, char *argv[]) return (file_fat_detectfs ()); } +#ifndef CONFIG_CMD_TREE_FAT U_BOOT_CMD( fatinfo, 3, 1, do_fat_fsinfo, "print information about filesystem", " \n" " - print information about filesystem from 'dev' on 'interface'" ); +#endif #ifdef NOT_IMPLEMENTED_YET /* find first device whose first partition is a DOS filesystem */ @@ -318,3 +814,85 @@ void hexdump (int cnt, unsigned char *data) } } #endif /* NOT_IMPLEMENTED_YET */ + + +#ifdef CONFIG_CMD_TREE_FAT +/* replace all fatXXX commands with 'fat xxx' command. + */ +int do_fat(cmd_tbl_t *cmdtp, int flag, int argc, char *argv[]) +{ + int (*fn)(cmd_tbl_t *cmdtp, int flag, int argc, char *argv[]); + fn = NULL; + argc--; + argv++; + + if (!strncmp(argv[0], "load", 5)) fn = do_fat_fsload; + else if (!strncmp(argv[0], "ls", 2)) fn = do_fat_ls; + else if (!strncmp(argv[0], "info", 5)) fn = do_fat_fsinfo; +#ifdef CONFIG_ROCKBOX_FAT + else if (!strncmp(argv[0], "write", 6)) fn = do_fat_fswrite; + else if (!strncmp(argv[0], "rm", 3)) fn = do_fat_fsrm; + else if (!strncmp(argv[0], "mkdir", 6)) fn = do_fat_fsmkdir; + else if (!strncmp(argv[0], "rmdir", 6)) fn = do_fat_fsrmdir; + else if (!strncmp(argv[0], "cd", 3)) fn = do_fat_fscd; + else if (!strncmp(argv[0], "pwd", 4)) fn = do_fat_fspwd; + else if (!strncmp(argv[0], "mv", 3)) fn = do_fat_fsmv; + else if (!strncmp(argv[0], "free", 5)) fn = do_fat_free; +#endif + if (fn) { + if(fn(cmdtp, flag, argc, argv) == 0) return 0; + return 1; + } + printf("unknown 'fat' command, check 'help fat'\n"); + return 1; +} + +U_BOOT_CMD( + fat, 7, 0, do_fat, + "some fat support commands\n", + "\n" + "fat load [bytes]\n" + " - load binary file 'filename' from 'dev' on 'interface'\n" + " to address 'addr' from dos filesystem\n" + "\n" + "fat ls [directory]\n" + " - list files from 'dev' on 'interface' in a 'directory'\n" + "\n" + "fat info \n" + " - print information about filesystem from 'dev' on 'interface'\n" + "\n" +#ifdef CONFIG_ROCKBOX_FAT + "fat write \n" + " - store binary file 'filename' to 'dev' on 'interface'\n" + " containing a dos filesystem from address 'addr'\n" + " with length 'bytes'\n" + "\n" + "fat rm \n" + " - remove 'filename' in the FAT16/32 filesytem\n" + " on 'dev' on 'interface'\n" + "\n" + "fat mkdir \n" + " - make 'dirname' in the FAT16/32 filesytem on 'dev'\n" + " on 'interface'\n" + "\n" + "fat rmdir \n" + " - remove 'dirname' in the FAT16/32 filesytem on 'dev'\n" + " on 'interface'\n" + "\n" + "fat cd \n" + " - change to 'dirname' in the FAT16/32 filesytem on 'dev'\n" + " on 'interface'\n" + "\n" + "fat pwd \n" + " - print the CWD in the FAT16/32 filesytem on 'dev'\n" + " on 'interface'\n" + "\n" + "fat mv \n" + " - change to in the FAT16/32 filesytem\n" + " on 'dev' on 'interface'\n" + "\n" + "fat free \n" + " - print free sapce available in filesystem (full partition scan)\n" +#endif +); +#endif diff --git a/common/unicode.c b/common/unicode.c new file mode 100644 index 000000000..90b862c5b --- /dev/null +++ b/common/unicode.c @@ -0,0 +1,441 @@ +/*************************************************************************** + * Copyright (c) 2004,2005 by Marcoen Hirschberg + * + * 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. + * + * 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 + * + * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY + * KIND, either express or implied. + * + **************************************************************************** + * Source file dumped from rockbox-3.1 distribution. + * $Id: unicode.c 19448 2008-12-15 23:42:19Z zagor $ + * Copyright (c) 2004,2005 by Marcoen Hirschberg + **************************************************************************** + * See file CREDITS for list of people who contributed to the U-boot + * project. + * + * 09-jan-2008 etienne.carriere@stnwireless.com - port from rockbox to U-boot + **************************************************************************** + * Some conversion functions for handling UTF-8 + * + * I got all the info from: + * http://www.cl.cam.ac.uk/~mgk25/unicode.html#utf-8 + * and + * http://en.wikipedia.org/wiki/Unicode + **************************************************************************** + */ +#include /* u-boot basics */ + +/*#include n.a for u-boot */ +/* #include "file.h" */ +/* #include "debug.h" */ +#include +/*#include "config.h" n.a for u-boot */ + +#ifndef O_BINARY +#define O_BINARY 0 +#endif + +/* U-boot port: no code page file accessible: remove code page ressources */ +#define UNICODE_NO_CP_TABLE +#define UNICODE_NO_CP_TABLE + + + +#ifndef UNICODE_NO_CP_TABLE + +#define CODEPAGE_DIR ROCKBOX_DIR"/codepages" +static int default_codepage = 0; +static int loaded_cp_table = 0; + +#ifdef HAVE_LCD_BITMAP + +#define MAX_CP_TABLE_SIZE 32768 +#define NUM_TABLES 5 + +static const char *filename[NUM_TABLES] = +{ + CODEPAGE_DIR"/iso.cp", + CODEPAGE_DIR"/932.cp", /* SJIS */ + CODEPAGE_DIR"/936.cp", /* GB2312 */ + CODEPAGE_DIR"/949.cp", /* KSX1001 */ + CODEPAGE_DIR"/950.cp" /* BIG5 */ +}; + +static const char cp_2_table[NUM_CODEPAGES] = +{ + 0, 1, 1, 1, 1, 1, 1, 1, 1, 2, 3, 4, 5, 0 +}; + +static const char *name_codepages[NUM_CODEPAGES+1] = +{ + "ISO-8859-1", + "ISO-8859-7", + "ISO-8859-8", + "CP1251", + "ISO-8859-11", + "CP1256", + "ISO-8859-9", + "ISO-8859-2", + "CP1250", + "SJIS", + "GB-2312", + "KSX-1001", + "BIG5", + "UTF-8", + "unknown" +}; + +#else /* !HAVE_LCD_BITMAP, reduced support */ + +#define MAX_CP_TABLE_SIZE 640 +#define NUM_TABLES 1 + +static const char *filename[NUM_TABLES] = { + CODEPAGE_DIR"/isomini.cp" +}; + +static const char cp_2_table[NUM_CODEPAGES] = +{ + 0, 1, 1, 1, 1, 1, 0 +}; + +static const char *name_codepages[NUM_CODEPAGES+1] = +{ + "ISO-8859-1", + "ISO-8859-7", + "CP1251", + "ISO-8859-9", + "ISO-8859-2", + "CP1250", + "UTF-8", + "unknown" +}; + +#endif + +static unsigned short codepage_table[MAX_CP_TABLE_SIZE]; + +#endif /* #ifndef UNICODE_NO_CP_TABLE */ + +static const unsigned char utf8comp[6] = +{ + 0x00, 0xC0, 0xE0, 0xF0, 0xF8, 0xFC +}; + +#ifndef UNICODE_NO_CP_TABLE +/* Load codepage file into memory */ +static int load_cp_table(int cp) +{ + int i=0; + int table = cp_2_table[cp]; + int file, tablesize; + unsigned char tmp[2]; + + if (table == 0 || table == loaded_cp_table) + return 1; + + file = open(filename[table-1], O_RDONLY|O_BINARY); + + if (file < 0) { + DEBUGF("Can't open codepage file: %s.cp\n", filename[table-1]); + return 0; + } + + tablesize = filesize(file) / 2; + + if (tablesize > MAX_CP_TABLE_SIZE) { + DEBUGF("Invalid codepage file: %s.cp\n", filename[table-1]); + close(file); + return 0; + } + + while (i < tablesize) { + if (!read(file, tmp, 2)) { + DEBUGF("Can't read from codepage file: %s.cp\n", + filename[table-1]); + loaded_cp_table = 0; + return 0; + } + codepage_table[i++] = (tmp[1] << 8) | tmp[0]; + } + + loaded_cp_table = table; + close(file); + return 1; +} +#endif /* #ifndef UNICODE_NO_CP_TABLE */ + +/* Encode a UCS value as UTF-8 and return a pointer after this UTF-8 char. */ +unsigned char* utf8encode(unsigned long ucs, unsigned char *utf8) +{ + int tail = 0; + + if (ucs > 0x7F) + while (ucs >> (5*tail + 6)) + tail++; + + *utf8++ = (ucs >> (6*tail)) | utf8comp[tail]; + while (tail--) + *utf8++ = ((ucs >> (6*tail)) & (MASK ^ 0xFF)) | COMP; + + return utf8; +} + +#ifndef UNICODE_NO_CP_TABLE +/* Recode an iso encoded string to UTF-8 */ +unsigned char* iso_decode(const unsigned char *iso, unsigned char *utf8, + int cp, int count) +{ + unsigned short ucs, tmp; + + if (cp == -1) /* use default codepage */ + cp = default_codepage; + + if (!load_cp_table(cp)) cp = 0; + + while (count--) { + if (*iso < 128 || cp == UTF_8) /* Already UTF-8 */ + *utf8++ = *iso++; + + else { + + /* cp tells us which codepage to convert from */ + switch (cp) { + case ISO_8859_7: /* Greek */ + case WIN_1251: /* Cyrillic */ + case ISO_8859_9: /* Turkish */ + case ISO_8859_2: /* Latin Extended */ + case WIN_1250: /* Central European */ +#ifdef HAVE_LCD_BITMAP + case ISO_8859_8: /* Hebrew */ + case ISO_8859_11: /* Thai */ + case WIN_1256: /* Arabic */ +#endif + tmp = ((cp-1)*128) + (*iso++ - 128); + ucs = codepage_table[tmp]; + break; + +#ifdef HAVE_LCD_BITMAP + case SJIS: /* Japanese */ + if (*iso > 0xA0 && *iso < 0xE0) { + tmp = *iso++ | (0xA100 - 0x8000); + ucs = codepage_table[tmp]; + break; + } + + case GB_2312: /* Simplified Chinese */ + case KSX_1001: /* Korean */ + case BIG_5: /* Traditional Chinese */ + if (count < 1 || !iso[1]) { + ucs = *iso++; + break; + } + + /* we assume all cjk strings are written + in big endian order */ + tmp = *iso++ << 8; + tmp |= *iso++; + tmp -= 0x8000; + ucs = codepage_table[tmp]; + count--; + break; +#endif /* HAVE_LCD_BITMAP */ + + default: + ucs = *iso++; + break; + } + + if (ucs == 0) /* unknown char, use replacement char */ + ucs = 0xfffd; + utf8 = utf8encode(ucs, utf8); + } + } + return utf8; +} +#else /* #ifndef UNICODE_NO_CP_TABLE */ +/* Recode an iso encoded string to UTF-8 : Support only default code page ISO_8859_1 */ +unsigned char* iso_decode(const unsigned char *iso, unsigned char *utf8, + int cp, int count) +{ + unsigned short ucs; + + if (cp == -1) cp = ISO_8859_1; /* use default codepage */ + if (cp != ISO_8859_1) { + printf("ERROR: unsupported codepage ID %d (see include/rbunicode.h)\n", cp); + cp = ISO_8859_1; + } + + while (count--) { + ucs = *iso++; + if (ucs == 0) ucs = 0xfffd; /* unknown char, use replacement char */ + utf8 = utf8encode(ucs, utf8); + } + return utf8; +} +#endif + +/* Recode a UTF-16 string with little-endian byte ordering to UTF-8 */ +unsigned char* utf16LEdecode(const unsigned char *utf16, unsigned char *utf8, + int count) +{ + unsigned long ucs; + + while (count > 0) { + /* Check for a surrogate pair */ + if (utf16[1] >= 0xD8 && utf16[1] < 0xE0) { + ucs = 0x10000 + ((utf16[0] << 10) | ((utf16[1] - 0xD8) << 18) + | utf16[2] | ((utf16[3] - 0xDC) << 8)); + utf16 += 4; + count -= 2; + } else { + ucs = (utf16[0] | (utf16[1] << 8)); + utf16 += 2; + count -= 1; + } + utf8 = utf8encode(ucs, utf8); + } + return utf8; +} + +/* Recode a UTF-16 string with big-endian byte ordering to UTF-8 */ +unsigned char* utf16BEdecode(const unsigned char *utf16, unsigned char *utf8, + int count) +{ + unsigned long ucs; + + while (count > 0) { + if (*utf16 >= 0xD8 && *utf16 < 0xE0) { /* Check for a surrogate pair */ + ucs = 0x10000 + (((utf16[0] - 0xD8) << 18) | (utf16[1] << 10) + | ((utf16[2] - 0xDC) << 8) | utf16[3]); + utf16 += 4; + count -= 2; + } else { + ucs = (utf16[0] << 8) | utf16[1]; + utf16 += 2; + count -= 1; + } + utf8 = utf8encode(ucs, utf8); + } + return utf8; +} + +#if 0 /* currently unused */ +/* Recode any UTF-16 string to UTF-8 */ +unsigned char* utf16decode(const unsigned char *utf16, unsigned char *utf8, + unsigned int count) +{ + unsigned long ucs; + + ucs = *(utf16++) << 8; + ucs |= *(utf16++); + + if (ucs == 0xFEFF) /* Check for BOM */ + return utf16BEdecode(utf16, utf8, count-1); + else if (ucs == 0xFFFE) + return utf16LEdecode(utf16, utf8, count-1); + else { /* ADDME: Should default be LE or BE? */ + utf16 -= 2; + return utf16BEdecode(utf16, utf8, count); + } +} +#endif + +/* Return the number of UTF-8 chars in a string */ +unsigned long utf8length(const unsigned char *utf8) +{ + unsigned long l = 0; + + while (*utf8 != 0) + if ((*utf8++ & MASK) != COMP) + l++; + + return l; +} + +/* Decode 1 UTF-8 char and return a pointer to the next char. */ +const unsigned char* utf8decode(const unsigned char *utf8, unsigned short *ucs) +{ + unsigned char c = *utf8++; + unsigned long code; + int tail = 0; + + if ((c <= 0x7f) || (c >= 0xc2)) { + /* Start of new character. */ + if (c < 0x80) { /* U-00000000 - U-0000007F, 1 byte */ + code = c; + } else if (c < 0xe0) { /* U-00000080 - U-000007FF, 2 bytes */ + tail = 1; + code = c & 0x1f; + } else if (c < 0xf0) { /* U-00000800 - U-0000FFFF, 3 bytes */ + tail = 2; + code = c & 0x0f; + } else if (c < 0xf5) { /* U-00010000 - U-001FFFFF, 4 bytes */ + tail = 3; + code = c & 0x07; + } else { + /* Invalid size. */ + code = 0xfffd; + } + + while (tail-- && ((c = *utf8++) != 0)) { + if ((c & 0xc0) == 0x80) { + /* Valid continuation character. */ + code = (code << 6) | (c & 0x3f); + + } else { + /* Invalid continuation char */ + code = 0xfffd; + utf8--; + break; + } + } + } else { + /* Invalid UTF-8 char */ + code = 0xfffd; + } + /* currently we don't support chars above U-FFFF */ + *ucs = (code < 0x10000) ? code : 0xfffd; + return utf8; +} + +#ifndef UNICODE_NO_CP_TABLE +void set_codepage(int cp) +{ + default_codepage = cp; + return; +} +#endif /* #ifndef UNICODE_NO_CP_TABLE */ + +/* seek to a given char in a utf8 string and + return its start position in the string */ +int utf8seek(const unsigned char* utf8, int offset) +{ + int pos = 0; + + while (offset--) { + pos++; + while ((utf8[pos] & MASK) == COMP) + pos++; + } + return pos; +} + +#ifndef UNICODE_NO_CP_TABLE +const char* get_codepage_name(int cp) +{ + if (cp < 0 || cp>= NUM_CODEPAGES) + return name_codepages[NUM_CODEPAGES]; + return name_codepages[cp]; +} +#endif /* #ifndef UNICODE_NO_CP_TABLE */ + -- cgit v1.2.3