1 /*
2   Copyright 1998-2003 Victor Wagner
3   Copyright 2003 Alex Ott
4   This file is released under the GPL.  Details can be
5   found in the file COPYING accompanying this distribution.
6 */
7 #ifdef HAVE_CONFIG_H
8 #include <config.h>
9 #endif
10 #include <unistd.h>
11 #include <stdio.h>
12 #include <string.h>
13 #include <sys/stat.h>
14 #include <stdlib.h>
15 #include "catdoc.h"
16 #if defined(MSDOS) && !defined(__MSDOS__)
17 #define __MSDOS__
18 #endif
19 #if defined(__MSDOS__) || defined(_WIN32)
20 #include <dir.h>
21 #include <dos.h>
22 #else
23 #include <glob.h>
24 #endif
25 
26 
27 /************************************************************************/
28 /*  Copies component of string starting with p and ending one char      */
29 /*  before q into path_buf, expanding ~ if neccessary                   */
30 /************************************************************************/
prepare_path_buf(char * path_buf,const char * start,const char * end)31 int prepare_path_buf(char *path_buf, const char *start, const char *end) {
32 	if (*start == '~' && start[1] == DIR_SEP) {
33 		char *home=getenv("HOME");
34 		start++;
35 		if (!home) {
36 			if (end-start>PATH_BUF_SIZE) return 0;
37 			strncpy(path_buf,start,end-start);
38 			path_buf[end-start]=0;
39 		} else {
40 			int l = strlen(home);
41 			if (l+(end-start)>PATH_BUF_SIZE) return 0;
42 			strcpy(path_buf,home);
43 			strncpy(path_buf+l,start,end-start);
44 			path_buf[end-start+l]=0;
45 		}
46 	} else {
47 			if (end-start>PATH_BUF_SIZE) return 0;
48 			strncpy(path_buf,start,end-start);
49 			path_buf[end-start]=0;
50 	}
51 	/* Empty list element means current directory */
52 	if (!*path_buf) {
53 		path_buf[0]='.';
54 		path_buf[1]=0;
55 #ifdef __MSDOS__
56 	} else {
57 		strcpy(path_buf,add_exe_path(path_buf)); /* safe, becouse
58 													add_exe_path knows about PATH_BUF_SIZE */
59 #endif
60 	}
61 	return 1;
62 
63 }
64 /************************************************************************/
65 /* Searches for file name in specified list of directories. Sets        */
66 /* Returns dynamically allocated full path or NULL. if nothing          */
67 /* appropriate   Expects name to be dynamically allocated and frees it  */
68 /************************************************************************/
find_file(char * name,const char * path)69 char *find_file(char *name, const char *path)
70 { const char *p;
71 	char *q;
72 	char path_buf[PATH_BUF_SIZE];
73 	char dir_sep[2]={DIR_SEP,0};
74 	for (p=path;p;p=(q?(q+1):NULL)) {
75 		q=strchr(p,LIST_SEP);
76 		if (q) {
77 			if (!prepare_path_buf(path_buf,p,q)) continue;
78 		} else {
79 			if (!prepare_path_buf(path_buf,p,p+strlen(p))) continue;
80 		}
81 		strcat(path_buf,dir_sep); /* always one char */
82 		if (strlen(path_buf)+strlen(name)>=PATH_BUF_SIZE)
83 			continue; /* Ignore too deeply nested directories */
84 		strcat(path_buf,name);
85 		if (access(path_buf,0)==0) {
86 			free(name);
87 			return strdup(path_buf);
88 		}
89 	}
90 	/* if we are here, nothing found */
91 	free(name);
92 	return NULL;
93 }
94 
95 /************************************************************************/
96 /* Searches for charset with given name and put pointer to malloced copy*/
97 /* of its name into first arg if found. Otherwise leaves first arg      */
98 /*  unchanged. Returns non-zero on success                              */
99 /************************************************************************/
check_charset(char ** filename,const char * charset)100 int check_charset(char **filename,const char *charset) {
101 	char *tmppath;
102 	if (charset == NULL ) {
103 		return 0;
104 	}
105 	if (!strncmp(charset,"utf-8",6)) {
106 		*filename=strdup("utf-8");
107 		return 1;
108 	}
109 	tmppath=find_file(stradd(charset,CHARSET_EXT),charset_path);
110 	if (tmppath && *tmppath) {
111 			*filename=strdup(charset);
112 			free(tmppath);
113 			return 1;
114 	}
115 	return 0;
116 }
117 
118 /**********************************************************************/
119 /*  Returns malloced string containing concatenation of two           */
120 /*  arguments                                                         */
121 /**********************************************************************/
stradd(const char * s1,const char * s2)122 char *stradd(const char *s1,const char *s2)
123 { char *res;
124 	res=malloc(strlen(s1)+strlen(s2)+1);
125 	if (!res) {
126 		fprintf (stderr,"Out of memory!");
127 		exit(1);
128 	}
129 	strcpy(res,s1);
130 	strcat(res,s2);
131 	return res;
132 }
133 
134 
135 /*
136  * In DOS, argv[0] contain full path to the program, and it is a custom
137  * to keep configuration files in same directory as program itself
138  */
139 #ifdef __MSDOS__
exe_dir(void)140 char *exe_dir(void) {
141 	static char pathbuf[PATH_BUF_SIZE];
142 	char *q;
143 	strcpy(pathbuf,_argv[0]); /* DOS ensures, that our exe path is no
144 								 longer than PATH_BUF_SIZE*/
145 	q=strrchr(pathbuf,DIR_SEP);
146 	if (q) {
147 		*q=0;
148 	} else {
149 		pathbuf[0]=0;
150 	}
151 	return pathbuf;
152 }
add_exe_path(const char * name)153 char *add_exe_path(const char *name) {
154 	static char path[PATH_BUF_SIZE];
155 	char *mypath=exe_dir();
156 	/* No snprintf in Turbo C 2.0 library, so just check by hand
157 	   and exit if something goes wrong */
158 	if (strchr(name,'%')) {
159 		/* there is substitution */
160 		if (strlen(name)-1+strlen(mypath)>=PATH_BUF_SIZE) {
161 			fprintf(stderr,"Invalid config file. file name \"%s\" too long "
162 					"after substitution\n",name);
163 			exit(1);
164 		}
165 		sprintf(path,name,exe_dir());
166 		return path;
167 	} else {
168 		return name;
169 	}
170 }
171 #endif
172 /*********************************************************************/
173 /* Prints out list of available charsets, i.e. names without extension *
174  * of all .txt files in the charset path + internally-supported utf-8  *
175  ************************************************************************/
176 
list_charsets(void)177 void list_charsets(void) {
178 	const char *p;
179 	char *q;
180 	char path_buf[PATH_BUF_SIZE];
181 	char dir_sep[2]={DIR_SEP,0};
182 	char **ptr;
183 #ifdef __MSDOS__
184 	struct ffblk ffblock;
185 	int res,col;
186 #else
187 	glob_t glob_buf;
188 	int count,glob_flags=GLOB_ERR;
189 
190 	memset(&glob_buf,0,sizeof(glob_t));
191 #endif
192 	for (p=charset_path;p;p=(q?(q+1):NULL)) {
193 		q=strchr(p,LIST_SEP);
194 		if (q) {
195 			if (q-p>=PATH_BUF_SIZE) {
196 				/* Oops, dir name too long, perhabs broken config file */
197 				continue;
198 			}
199 			strncpy(path_buf,p,q-p);
200 			path_buf[q-p]=0;
201 		} else {
202 			if (strlen(p)>=PATH_BUF_SIZE) continue;
203 			strcpy(path_buf,p);
204 		}
205 		/* Empty list element means current directory */
206 		if (!*path_buf) {
207 			path_buf[0]='.';
208 			path_buf[1]=0;
209 #ifdef __MSDOS__
210 		} else {
211 			strcpy(path_buf,add_exe_path(path_buf)); /* safe, becouse
212 														add_exe_path knows about PATH_BUF_SIZE */
213 #endif
214 		}
215 		strcat(path_buf,dir_sep); /* always one char */
216 		if (strlen(path_buf)+6>=PATH_BUF_SIZE)
217 			continue; /* Ignore too deeply nested directories */
218 		strcat(path_buf,"*.txt");
219 #ifdef __MSDOS__
220 		res=findfirst(path_buf,&ffblock,FA_RDONLY | FA_HIDDEN | FA_ARCH);
221 		col=1;
222 		printf("Available charsets:\n");
223 		while (!res) {
224 			char name[12],*src,*dest;
225 			dest=name;
226 			src=ffblock.ff_name;
227 			for (dest=name,src=ffblock.ff_name;*src && *src !='.';dest++,src++)
228 				*dest=tolower(*src);
229 			*dest++=(col<5)?'\t':'\n';
230 			if (++col>5) col=1;
231 			*dest=0;
232 			printf("%10s",name);
233 			res=findnext(&ffblock);
234 		}
235 #else
236 		switch (glob(path_buf,glob_flags,NULL,&glob_buf)) {
237 			case 0:
238 #ifdef GLOB_NOMATCH
239 			case GLOB_NOMATCH:
240 #endif
241 				break;
242 			default:
243 				perror("catdoc");
244 				exit(1);
245 		}
246 		glob_flags|=GLOB_APPEND;
247 #endif
248 	}
249 #ifdef __MSDOS__
250 	fputs("utf-8\n",stdout);
251 #else
252 	count=0;printf("Available charsets:");
253 	for (ptr=glob_buf.gl_pathv;*ptr;ptr++) {
254 		printf("%c",(count++)%5?'\t':'\n');
255 		p=strrchr(*ptr,dir_sep[0]);
256 		if (!p) continue;
257 		p++;
258 		if ((q=strchr(p,'.'))) *q=0;
259 		fputs(p,stdout);
260 	}
261 	printf("%c",(count++)%5?'\t':'\n');
262 	fputs("utf-8",stdout);
263 	printf("\n");
264 	globfree(&glob_buf);
265 #endif
266 }
267