d6c054575b
git-svn-id: svn://svn.rockbox.org/rockbox/trunk@7416 a1c6a512-1295-4272-9138-f99709370657
791 lines
18 KiB
C
791 lines
18 KiB
C
/***************************************************************************
|
|
* __________ __ ___.
|
|
* Open \______ \ ____ ____ | | _\_ |__ _______ ___
|
|
* Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
|
|
* Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
|
|
* Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
|
|
*
|
|
* $Id$
|
|
*
|
|
* Copyright (C) 2004 Henrik Backe
|
|
*
|
|
* All files in this archive are subject to the GNU General Public License.
|
|
* See the file COPYING in the source tree root for full license agreement.
|
|
*
|
|
* This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
|
|
* KIND, either express or implied.
|
|
*
|
|
****************************************************************************/
|
|
|
|
#include <stdio.h>
|
|
#include <string.h>
|
|
#include <stdlib.h>
|
|
#include <stdbool.h>
|
|
|
|
#include "sprintf.h"
|
|
#include "settings.h"
|
|
#include "debug.h"
|
|
#include "lang.h"
|
|
#include "language.h"
|
|
#include "kernel.h"
|
|
#include "plugin.h"
|
|
#include "filetypes.h"
|
|
#include "screens.h"
|
|
#include "icons.h"
|
|
#include "dir.h"
|
|
#include "file.h"
|
|
#include "icons.h"
|
|
|
|
/* max plugin name size without extensions and path */
|
|
#define MAX_PLUGIN_LENGTH 32
|
|
|
|
/* max filetypes (plugins & icons stored here) */
|
|
#if CONFIG_CODEC == SWCODEC
|
|
#define MAX_FILETYPES 64
|
|
#else
|
|
#define MAX_FILETYPES 32
|
|
#endif
|
|
|
|
/* max exttypes (extensions stored here) */
|
|
#if CONFIG_CODEC == SWCODEC
|
|
/* Software codecs require more file extensions */
|
|
#define MAX_EXTTYPES 64
|
|
#else
|
|
#define MAX_EXTTYPES 32
|
|
#endif
|
|
|
|
/* string buffer length */
|
|
#define STRING_BUFFER_SIZE 512
|
|
|
|
/* number of bytes for the binary icon */
|
|
#define ICON_LENGTH 6
|
|
|
|
/* mask for dynamic filetype info in attribute */
|
|
#define FILETYPES_MASK 0xFF00
|
|
|
|
/* filenames */
|
|
#define ROCK_EXTENSION ".rock"
|
|
#define VIEWERS_CONFIG ROCKBOX_DIR "/viewers.config"
|
|
#define VIEWERS_DIR ROCKBOX_DIR "/viewers"
|
|
|
|
/* global variables */
|
|
static int cnt_filetypes;
|
|
static int cnt_exttypes;
|
|
static struct ext_type exttypes [MAX_EXTTYPES];
|
|
static struct file_type filetypes[MAX_FILETYPES];
|
|
static int first_soft_exttype;
|
|
static int first_soft_filetype;
|
|
static char* next_free_string;
|
|
static char plugin_name[sizeof(VIEWERS_DIR) + 7 + MAX_PLUGIN_LENGTH];
|
|
static char string_buffer[STRING_BUFFER_SIZE];
|
|
|
|
/* prototypes */
|
|
#ifdef HAVE_LCD_BITMAP
|
|
static char* string2icon(const char*);
|
|
static int add_plugin(char*,char*);
|
|
#else
|
|
static int add_plugin(char*);
|
|
#endif
|
|
static char* get_string(const char*);
|
|
static int find_attr_index(int);
|
|
static bool read_config(const char*);
|
|
static void rm_whitespaces(char*);
|
|
static void scan_plugins(void);
|
|
|
|
/* initialize dynamic filetypes (called at boot from tree.c) */
|
|
void filetype_init(void)
|
|
{
|
|
int cnt,i,ix;
|
|
const struct filetype* ftypes;
|
|
|
|
memset(exttypes,0,sizeof(exttypes));
|
|
memset(filetypes,0,sizeof(filetypes));
|
|
next_free_string=string_buffer;
|
|
|
|
/* The special filetype folder must always be stored at index 0 */
|
|
#ifdef HAVE_LCD_BITMAP
|
|
if (!filetypes[0].icon)
|
|
filetypes[0].icon = bitmap_icons_6x8[Icon_Folder];
|
|
#else
|
|
if (!filetypes[0].icon)
|
|
filetypes[0].icon = Icon_Folder;
|
|
for (i=1; i < MAX_FILETYPES; i++)
|
|
filetypes[i].icon = -1;
|
|
#endif
|
|
|
|
/* register hardcoded filetypes */
|
|
tree_get_filetypes(&ftypes, &cnt);
|
|
cnt_exttypes=0;
|
|
cnt_filetypes=0;
|
|
|
|
for (i = 0; i < cnt ; i++)
|
|
{
|
|
ix = ((ftypes[i].tree_attr & FILETYPES_MASK) >> 8);
|
|
if (ix < MAX_FILETYPES && i < MAX_EXTTYPES)
|
|
{
|
|
#ifdef HAVE_LCD_BITMAP
|
|
if (filetypes[ix].icon == NULL)
|
|
filetypes[ix].icon=bitmap_icons_6x8[ftypes[i].icon];
|
|
#else
|
|
if (filetypes[ix].icon == -1)
|
|
filetypes[ix].icon=ftypes[i].icon;
|
|
#endif
|
|
if (ix > cnt_filetypes)
|
|
cnt_filetypes=ix;
|
|
exttypes[cnt_exttypes].type=&filetypes[ix];
|
|
exttypes[cnt_exttypes].extension=ftypes[i].extension;
|
|
cnt_exttypes++;
|
|
}
|
|
}
|
|
first_soft_exttype=cnt_exttypes;
|
|
cnt_filetypes++;
|
|
first_soft_filetype=cnt_filetypes;
|
|
|
|
/* register dynamic filetypes */
|
|
read_config(VIEWERS_CONFIG);
|
|
scan_plugins();
|
|
}
|
|
|
|
/* get icon */
|
|
#ifdef HAVE_LCD_BITMAP
|
|
const char* filetype_get_icon(int attr)
|
|
#else
|
|
int filetype_get_icon(int attr)
|
|
#endif
|
|
{
|
|
int ix;
|
|
|
|
ix = find_attr_index(attr);
|
|
|
|
if (ix < 0)
|
|
{
|
|
#ifdef HAVE_LCD_BITMAP
|
|
return NULL;
|
|
#else
|
|
return -1;
|
|
#endif
|
|
}
|
|
else
|
|
{
|
|
return filetypes[ix].icon;
|
|
}
|
|
}
|
|
|
|
/* get plugin */
|
|
char* filetype_get_plugin(const struct entry* file)
|
|
{
|
|
int ix;
|
|
|
|
ix=find_attr_index(file->attr);
|
|
|
|
if (ix < 0)
|
|
{
|
|
return NULL;
|
|
}
|
|
|
|
if ((filetypes[ix].plugin == NULL) ||
|
|
(strlen(filetypes[ix].plugin) > MAX_PLUGIN_LENGTH))
|
|
return NULL;
|
|
|
|
snprintf(plugin_name, sizeof(plugin_name),
|
|
VIEWERS_DIR "/%s.rock",filetypes[ix].plugin);
|
|
|
|
return plugin_name;
|
|
}
|
|
|
|
/* check if filetype is supported */
|
|
bool filetype_supported(int attr)
|
|
{
|
|
int ix;
|
|
|
|
ix=find_attr_index(attr);
|
|
|
|
/* hard filetypes and soft filetypes with plugins is supported */
|
|
if (ix > 0)
|
|
if (filetypes[ix].plugin || ix < first_soft_filetype)
|
|
return true;
|
|
|
|
return false;
|
|
}
|
|
|
|
/* get the "dynamic" attribute for an extension */
|
|
int filetype_get_attr(const char* name)
|
|
{
|
|
int i;
|
|
|
|
for (i=0; i < cnt_exttypes; i++)
|
|
{
|
|
if (exttypes[i].extension)
|
|
{
|
|
if (!strcasecmp(&name[strlen(name)-
|
|
strlen(exttypes[i].extension)],
|
|
exttypes[i].extension))
|
|
{
|
|
return ((((unsigned long)exttypes[i].type -
|
|
(unsigned long)&filetypes[0]) /
|
|
sizeof(struct file_type)) << 8);
|
|
}
|
|
}
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
/* fill a menu list with viewers (used in onplay.c) */
|
|
int filetype_load_menu(struct menu_item* menu,int max_items)
|
|
{
|
|
int i;
|
|
int cnt=0;
|
|
|
|
for (i=0; i < cnt_filetypes; i++)
|
|
{
|
|
if (filetypes[i].plugin)
|
|
{
|
|
menu[cnt].desc = filetypes[i].plugin;
|
|
cnt++;
|
|
if (cnt == max_items)
|
|
break;
|
|
}
|
|
}
|
|
return cnt;
|
|
}
|
|
|
|
/* start a plugin with an argument (called from onplay.c) */
|
|
int filetype_load_plugin(const char* plugin, char* file)
|
|
{
|
|
snprintf(plugin_name,sizeof(plugin_name),"%s/%s.rock",
|
|
VIEWERS_DIR,plugin);
|
|
return plugin_load(plugin_name,file);
|
|
}
|
|
|
|
/* get index to filetypes[] from the file attribute */
|
|
static int find_attr_index(int attr)
|
|
{
|
|
int ix;
|
|
ix = ((attr & FILETYPES_MASK) >> 8);
|
|
|
|
if ((attr & ATTR_DIRECTORY)==ATTR_DIRECTORY)
|
|
{
|
|
ix=0;
|
|
}
|
|
else
|
|
{
|
|
if (ix==0)
|
|
ix=-1;
|
|
if (ix > cnt_filetypes)
|
|
ix=-1;
|
|
else
|
|
if ((filetypes[ix].plugin == NULL) &&
|
|
#ifdef HAVE_LCD_BITMAP
|
|
(filetypes[ix].icon == NULL)
|
|
#else
|
|
(filetypes[ix].icon == -1)
|
|
#endif
|
|
)
|
|
ix=-1;
|
|
}
|
|
|
|
return ix;
|
|
}
|
|
|
|
/* scan the plugin directory and register filetypes */
|
|
static void scan_plugins(void)
|
|
{
|
|
DIR *dir;
|
|
struct dirent *entry;
|
|
char* cp;
|
|
char* dot;
|
|
char* dash;
|
|
int ix;
|
|
int i;
|
|
bool found;
|
|
|
|
dir = opendir(VIEWERS_DIR);
|
|
if(!dir)
|
|
return;
|
|
|
|
while (true)
|
|
{
|
|
/* exttypes[] full, bail out */
|
|
if (cnt_exttypes >= MAX_EXTTYPES)
|
|
{
|
|
splash(HZ,true,str(LANG_FILETYPES_EXTENSION_FULL));
|
|
break;
|
|
}
|
|
|
|
/* filetypes[] full, bail out */
|
|
if (cnt_filetypes >= MAX_FILETYPES)
|
|
{
|
|
splash(HZ,true,str(LANG_FILETYPES_FULL));
|
|
break;
|
|
}
|
|
|
|
entry = readdir(dir);
|
|
|
|
if (!entry)
|
|
break;
|
|
|
|
/* skip directories */
|
|
if ((entry->attribute & ATTR_DIRECTORY))
|
|
continue;
|
|
|
|
/* Skip FAT volume ID */
|
|
if (entry->attribute & ATTR_VOLUME_ID)
|
|
continue;
|
|
|
|
/* filter out dotfiles and hidden files */
|
|
if ((entry->d_name[0]=='.') ||
|
|
(entry->attribute & ATTR_HIDDEN)) {
|
|
continue;
|
|
}
|
|
|
|
/* filter out non rock files */
|
|
if (strcasecmp(
|
|
&entry->d_name[strlen(entry->d_name) - sizeof(ROCK_EXTENSION) + 1],
|
|
ROCK_EXTENSION)) {
|
|
continue;
|
|
}
|
|
|
|
/* filter out to long filenames */
|
|
if (strlen(entry->d_name) > MAX_PLUGIN_LENGTH + 5)
|
|
{
|
|
splash(HZ,true,str(LANG_FILETYPES_PLUGIN_NAME_LONG));
|
|
continue;
|
|
}
|
|
|
|
dot=strrchr(entry->d_name,'.');
|
|
*dot='\0';
|
|
dash=strchr(entry->d_name,'-');
|
|
|
|
/* add plugin and extension */
|
|
if (dash)
|
|
{
|
|
*dash='\0';
|
|
ix=(filetype_get_attr(entry->d_name) >> 8);
|
|
if (!ix)
|
|
{
|
|
cp=get_string(entry->d_name);
|
|
if (cp)
|
|
{
|
|
exttypes[cnt_exttypes].extension=cp;
|
|
exttypes[cnt_exttypes].type=&filetypes[cnt_filetypes];
|
|
#ifdef HAVE_LCD_BITMAP
|
|
exttypes[cnt_exttypes].type->icon = bitmap_icons_6x8[Icon_Plugin];
|
|
#else
|
|
exttypes[cnt_exttypes].type->icon = Icon_Plugin;
|
|
#endif
|
|
cnt_exttypes++;
|
|
|
|
*dash='-';
|
|
cp=get_string(entry->d_name);
|
|
if (cp)
|
|
{
|
|
filetypes[cnt_filetypes].plugin=cp;
|
|
cnt_filetypes++;
|
|
}
|
|
else
|
|
break;
|
|
}
|
|
else
|
|
break;
|
|
}
|
|
else
|
|
{
|
|
*dash='-';
|
|
if (!filetypes[ix].plugin)
|
|
{
|
|
cp=get_string(entry->d_name);
|
|
if (cp)
|
|
{
|
|
filetypes[cnt_filetypes].plugin=cp;
|
|
cnt_filetypes++;
|
|
}
|
|
else
|
|
break;
|
|
}
|
|
}
|
|
*dash='-';
|
|
}
|
|
/* add plugin only */
|
|
else
|
|
{
|
|
found=false;
|
|
for (i = first_soft_filetype; i < cnt_filetypes; i++)
|
|
{
|
|
if (filetypes[i].plugin)
|
|
if (!strcasecmp(filetypes[i].plugin,entry->d_name))
|
|
{
|
|
found=true;
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (!found)
|
|
{
|
|
cp=get_string(entry->d_name);
|
|
if (cp)
|
|
{
|
|
filetypes[cnt_filetypes].plugin=cp;
|
|
filetypes[cnt_filetypes].no_extension=true;
|
|
cnt_filetypes++;
|
|
}
|
|
else
|
|
break;
|
|
}
|
|
}
|
|
*dot='.';
|
|
}
|
|
closedir(dir);
|
|
}
|
|
|
|
#ifdef HAVE_LCD_BITMAP
|
|
static int add_plugin(char *plugin, char *icon)
|
|
#else
|
|
static int add_plugin(char *plugin)
|
|
#endif
|
|
{
|
|
char *cp;
|
|
int i;
|
|
|
|
if (!plugin)
|
|
return 0;
|
|
|
|
cp=strrchr(plugin, '.');
|
|
if (cp)
|
|
*cp='\0';
|
|
|
|
for (i=first_soft_filetype; i < cnt_filetypes; i++)
|
|
{
|
|
if (filetypes[i].plugin)
|
|
{
|
|
if (!strcasecmp(plugin, filetypes[i].plugin))
|
|
{
|
|
#ifdef HAVE_LCD_BITMAP
|
|
if (filetypes[i].icon == NULL && icon)
|
|
{
|
|
cp = string2icon(icon);
|
|
if (cp)
|
|
filetypes[cnt_filetypes].icon = cp;
|
|
else
|
|
return 0;
|
|
}
|
|
#endif
|
|
return i;
|
|
}
|
|
}
|
|
}
|
|
|
|
/* new plugin */
|
|
cp = get_string(plugin);
|
|
if (cp)
|
|
{
|
|
filetypes[cnt_filetypes].plugin = cp;
|
|
#ifdef HAVE_LCD_BITMAP
|
|
/* add icon */
|
|
if (icon)
|
|
{
|
|
cp = string2icon(icon);
|
|
if (cp)
|
|
filetypes[cnt_filetypes].icon = cp;
|
|
else
|
|
return 0;
|
|
}
|
|
#endif
|
|
}
|
|
else
|
|
{
|
|
return 0;
|
|
}
|
|
|
|
cnt_filetypes++;
|
|
return cnt_filetypes - 1;
|
|
}
|
|
|
|
/* read config file (or cahe file) */
|
|
bool read_config(const char* file)
|
|
{
|
|
enum {extension,
|
|
plugin,
|
|
#ifdef HAVE_LCD_BITMAP
|
|
icon,
|
|
#endif
|
|
last};
|
|
|
|
int i,ix;
|
|
int fd;
|
|
char* end;
|
|
char* cp;
|
|
char* str[last];
|
|
char buf[80];
|
|
|
|
fd = open(file, O_RDONLY);
|
|
if (fd < 0)
|
|
return false;
|
|
|
|
while (read_line(fd, buf, sizeof(buf)))
|
|
{
|
|
if (cnt_exttypes >= MAX_EXTTYPES)
|
|
{
|
|
splash(HZ,true,str(LANG_FILETYPES_EXTENSION_FULL));
|
|
break;
|
|
}
|
|
|
|
if (cnt_filetypes >= MAX_FILETYPES)
|
|
{
|
|
splash(HZ,true,str(LANG_FILETYPES_FULL));
|
|
break;
|
|
}
|
|
|
|
/* parse buffer */
|
|
rm_whitespaces(buf);
|
|
|
|
if (strlen(buf) == 0)
|
|
continue;
|
|
|
|
if (buf[0] == '#')
|
|
continue;
|
|
|
|
memset(str,0,sizeof(str));
|
|
i=0;
|
|
cp=buf;
|
|
while (*cp==',') {
|
|
cp++;
|
|
i++;
|
|
}
|
|
str[i] = strtok_r(cp, ",", &end);
|
|
i++;
|
|
|
|
while (end && i < last)
|
|
{
|
|
if (end)
|
|
{
|
|
cp=end;
|
|
while (*cp==',') {
|
|
cp++;
|
|
i++;
|
|
}
|
|
}
|
|
str[i] = strtok_r(NULL, ",", &end);
|
|
if (str[i])
|
|
if (!strlen(str[i]))
|
|
str[i]=NULL;
|
|
i++;
|
|
}
|
|
|
|
/* bail out if no icon and no plugin */
|
|
if (!str[plugin]
|
|
#ifdef HAVE_LCD_BITMAP
|
|
&& !str[icon]
|
|
#endif
|
|
)
|
|
continue;
|
|
|
|
/* bail out if no plugin and icon is incorrect*/
|
|
if (!str[plugin]
|
|
#ifdef HAVE_LCD_BITMAP
|
|
&& strlen(str[icon]) != ICON_LENGTH*2
|
|
#endif
|
|
)
|
|
continue;
|
|
|
|
/* bail out if no icon and no plugin and no extension*/
|
|
if (!str[plugin] &&
|
|
#ifdef HAVE_LCD_BITMAP
|
|
!str[icon] &&
|
|
#endif
|
|
!str[extension])
|
|
continue;
|
|
|
|
/* bail out if we are not able to start plugin from onplay.c ?*/
|
|
if (str[plugin])
|
|
{
|
|
if (strlen(str[plugin]) > MAX_PLUGIN_LENGTH)
|
|
{
|
|
splash(HZ, true, str(LANG_FILETYPES_PLUGIN_NAME_LONG));
|
|
str[plugin] = NULL;
|
|
continue;
|
|
}
|
|
}
|
|
|
|
ix=0;
|
|
/* if extension already exist don't add a new one */
|
|
for (i=0; i < cnt_exttypes; i++)
|
|
{
|
|
if (!strcasecmp(str[extension],exttypes[i].extension))
|
|
{
|
|
#ifdef HAVE_LCD_BITMAP
|
|
ix=add_plugin(str[plugin],NULL);
|
|
if (ix)
|
|
{
|
|
if (str[icon] && filetypes[ix].icon == NULL)
|
|
{
|
|
if (exttypes[i].type->icon == NULL)
|
|
{
|
|
cp = string2icon(str[icon]);
|
|
if (cp)
|
|
exttypes[i].type->icon = cp;
|
|
}
|
|
}
|
|
}
|
|
#else
|
|
ix=add_plugin(str[plugin]);
|
|
#endif
|
|
if (exttypes[i].type == NULL)
|
|
{
|
|
exttypes[i].type = &filetypes[ix];
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
if (ix)
|
|
continue;
|
|
|
|
/* add extension */
|
|
if (str[extension])
|
|
{
|
|
#ifdef HAVE_LCD_BITMAP
|
|
ix=add_plugin(str[plugin],str[icon]);
|
|
#else
|
|
ix=add_plugin(str[plugin]);
|
|
#endif
|
|
if (ix)
|
|
{
|
|
cp=get_string(str[extension]);
|
|
if (cp)
|
|
{
|
|
exttypes[cnt_exttypes].extension = cp;
|
|
|
|
exttypes[cnt_exttypes].type = &filetypes[ix];
|
|
cnt_exttypes++;
|
|
filetypes[i].no_extension=false;
|
|
}
|
|
else
|
|
{
|
|
break;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
break;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
#ifdef HAVE_LCD_BITMAP
|
|
ix=add_plugin(str[plugin],str[icon]);
|
|
#else
|
|
ix=add_plugin(str[plugin]);
|
|
#endif
|
|
filetypes[ix].no_extension=true;
|
|
if (!i)
|
|
break;
|
|
}
|
|
}
|
|
close(fd);
|
|
|
|
return true;
|
|
}
|
|
|
|
#ifdef HAVE_LCD_BITMAP
|
|
/* convert an ascii hexadecimal icon to a binary icon */
|
|
static char* string2icon(const char* str)
|
|
{
|
|
char tmp[ICON_LENGTH*2];
|
|
char *cp;
|
|
int i;
|
|
|
|
if (strlen(str)!=ICON_LENGTH*2)
|
|
return NULL;
|
|
|
|
if ((sizeof(string_buffer) +
|
|
(unsigned long) string_buffer -
|
|
(unsigned long) next_free_string) < ICON_LENGTH)
|
|
{
|
|
splash(HZ,true,str(LANG_FILETYPES_STRING_BUFFER_EMPTY));
|
|
return NULL;
|
|
}
|
|
|
|
for (i=0; i<12; i++)
|
|
{
|
|
if (str[i] >= '0' && str[i] <= '9')
|
|
{
|
|
tmp[i]=str[i]-'0';
|
|
continue;
|
|
}
|
|
|
|
if (str[i] >= 'a' && str[i] <= 'f')
|
|
{
|
|
tmp[i]=str[i]-'a'+10;
|
|
continue;
|
|
}
|
|
|
|
if (str[i] >= 'A' && str[i] <= 'F')
|
|
{
|
|
tmp[i]=str[i]-'A'+10;
|
|
continue;
|
|
}
|
|
|
|
return NULL;
|
|
}
|
|
|
|
cp=next_free_string;
|
|
for (i = 0; i < ICON_LENGTH; i++)
|
|
cp[i]=((tmp[i*2]<<4) | tmp[i*2+1]);
|
|
|
|
next_free_string=&next_free_string[ICON_LENGTH];
|
|
return cp;
|
|
}
|
|
#endif
|
|
|
|
/* get string from buffer */
|
|
static char* get_string(const char* str)
|
|
{
|
|
unsigned int l=strlen(str)+1;
|
|
char* cp;
|
|
|
|
if (!str)
|
|
return NULL;
|
|
|
|
if (l <= (sizeof(string_buffer) +
|
|
(unsigned long) string_buffer -
|
|
(unsigned long) next_free_string))
|
|
{
|
|
strcpy(next_free_string,str);
|
|
cp=next_free_string;
|
|
next_free_string=&next_free_string[l];
|
|
return cp;
|
|
}
|
|
else
|
|
{
|
|
splash(HZ,true,str(LANG_FILETYPES_STRING_BUFFER_EMPTY));
|
|
return NULL;
|
|
}
|
|
}
|
|
|
|
/* remove all white spaces from string */
|
|
static void rm_whitespaces(char* str)
|
|
{
|
|
char *cp, *free;
|
|
|
|
cp=str;
|
|
free=cp;
|
|
|
|
while (cp < &str[strlen(str)])
|
|
{
|
|
switch (*cp)
|
|
{
|
|
case ' ' :
|
|
case '\t' :
|
|
case '\r' :
|
|
break;
|
|
|
|
default:
|
|
*free=*cp;
|
|
free++;
|
|
break;
|
|
}
|
|
cp++;
|
|
}
|
|
|
|
*free='\0';
|
|
}
|