Added some SD files which are needed by "contrib".
[AROS.git] / tools / flexcat / src / utils.c
blob5969d7aac63e01549c9e88446ae95ec017ef17b6
1 /*
2 * $Id$
4 * Copyright (C) 1993-1999 by Jochen Wiedmann and Marcin Orlowski
5 * Copyright (C) 2002-2010 by the FlexCat Open Source Team
7 * This program is free software; you can redistribute it and/or modify
8 * it under the terms of the GNU General Public License as published by
9 * the Free Software Foundation; either version 2 of the License, or (at
10 * your option) any later version.
12 * This program is distributed in the hope that it will be useful, but
13 * WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15 * General Public License for more details.
17 * You should have received a copy of the GNU General Public License
18 * along with this program; if not, write to the Free Software
19 * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
23 #if defined(AMIGA)
24 #include <proto/codesets.h>
25 #else
26 #include <iconv.h>
27 #endif
29 #include "flexcat.h"
30 #include "readprefs.h"
31 #include "swapfuncs.h"
32 #include "showfuncs.h"
33 #include "scancd.h"
34 #include "scanct.h"
35 #include "createcat.h"
36 #include "globals.h"
37 #include "utils.h"
38 #include "openlibs.h"
39 #include "SDI_compiler.h"
41 const char VString[] = VERS " [" SYSTEMSHORT "/" CPU "] (" EXE_DATE ")\n" EXE_COPYRIGHT;
42 const char EString[] = "Contact: http://sf.net/p/flexcat/";
44 /// MyExit
46 void MyExit(int Code)
48 #ifdef AMIGA
49 if((NumberOfWarnings > 0 || Code != 0) && !NoBeep)
50 DisplayBeep(NULL);
51 #endif
52 CloseFlexCatCatalog();
53 CloseLibs();
54 #ifdef NOERRORONWARN
55 if (Code < 10)
56 Code = 0;
57 #endif
58 exit(Code);
61 ///
63 #ifndef AMIGA
66 * This array is designed for mapping upper and lower case letters
67 * together for a case independent comparison. The mappings are
68 * based upon ascii character sequences.
71 typedef unsigned char uc;
72 static const unsigned char charmap[] =
74 (uc)'\000', (uc)'\001', (uc)'\002', (uc)'\003', (uc)'\004', (uc)'\005', (uc)'\006', (uc)'\007',
75 (uc)'\010', (uc)'\011', (uc)'\012', (uc)'\013', (uc)'\014', (uc)'\015', (uc)'\016', (uc)'\017',
76 (uc)'\020', (uc)'\021', (uc)'\022', (uc)'\023', (uc)'\024', (uc)'\025', (uc)'\026', (uc)'\027',
77 (uc)'\030', (uc)'\031', (uc)'\032', (uc)'\033', (uc)'\034', (uc)'\035', (uc)'\036', (uc)'\037',
78 (uc)'\040', (uc)'\041', (uc)'\042', (uc)'\043', (uc)'\044', (uc)'\045', (uc)'\046', (uc)'\047',
79 (uc)'\050', (uc)'\051', (uc)'\052', (uc)'\053', (uc)'\054', (uc)'\055', (uc)'\056', (uc)'\057',
80 (uc)'\060', (uc)'\061', (uc)'\062', (uc)'\063', (uc)'\064', (uc)'\065', (uc)'\066', (uc)'\067',
81 (uc)'\070', (uc)'\071', (uc)'\072', (uc)'\073', (uc)'\074', (uc)'\075', (uc)'\076', (uc)'\077',
82 (uc)'\100', (uc)'\141', (uc)'\142', (uc)'\143', (uc)'\144', (uc)'\145', (uc)'\146', (uc)'\147',
83 (uc)'\150', (uc)'\151', (uc)'\152', (uc)'\153', (uc)'\154', (uc)'\155', (uc)'\156', (uc)'\157',
84 (uc)'\160', (uc)'\161', (uc)'\162', (uc)'\163', (uc)'\164', (uc)'\165', (uc)'\166', (uc)'\167',
85 (uc)'\170', (uc)'\171', (uc)'\172', (uc)'\133', (uc)'\134', (uc)'\135', (uc)'\136', (uc)'\137',
86 (uc)'\140', (uc)'\141', (uc)'\142', (uc)'\143', (uc)'\144', (uc)'\145', (uc)'\146', (uc)'\147',
87 (uc)'\150', (uc)'\151', (uc)'\152', (uc)'\153', (uc)'\154', (uc)'\155', (uc)'\156', (uc)'\157',
88 (uc)'\160', (uc)'\161', (uc)'\162', (uc)'\163', (uc)'\164', (uc)'\165', (uc)'\166', (uc)'\167',
89 (uc)'\170', (uc)'\171', (uc)'\172', (uc)'\173', (uc)'\174', (uc)'\175', (uc)'\176', (uc)'\177',
90 (uc)'\200', (uc)'\201', (uc)'\202', (uc)'\203', (uc)'\204', (uc)'\205', (uc)'\206', (uc)'\207',
91 (uc)'\210', (uc)'\211', (uc)'\212', (uc)'\213', (uc)'\214', (uc)'\215', (uc)'\216', (uc)'\217',
92 (uc)'\220', (uc)'\221', (uc)'\222', (uc)'\223', (uc)'\224', (uc)'\225', (uc)'\226', (uc)'\227',
93 (uc)'\230', (uc)'\231', (uc)'\232', (uc)'\233', (uc)'\234', (uc)'\235', (uc)'\236', (uc)'\237',
94 (uc)'\240', (uc)'\241', (uc)'\242', (uc)'\243', (uc)'\244', (uc)'\245', (uc)'\246', (uc)'\247',
95 (uc)'\250', (uc)'\251', (uc)'\252', (uc)'\253', (uc)'\254', (uc)'\255', (uc)'\256', (uc)'\257',
96 (uc)'\260', (uc)'\261', (uc)'\262', (uc)'\263', (uc)'\264', (uc)'\265', (uc)'\266', (uc)'\267',
97 (uc)'\270', (uc)'\271', (uc)'\272', (uc)'\273', (uc)'\274', (uc)'\275', (uc)'\276', (uc)'\277',
98 (uc)'\300', (uc)'\341', (uc)'\342', (uc)'\343', (uc)'\344', (uc)'\345', (uc)'\346', (uc)'\347',
99 (uc)'\350', (uc)'\351', (uc)'\352', (uc)'\353', (uc)'\354', (uc)'\355', (uc)'\356', (uc)'\357',
100 (uc)'\360', (uc)'\361', (uc)'\362', (uc)'\363', (uc)'\364', (uc)'\365', (uc)'\366', (uc)'\367',
101 (uc)'\370', (uc)'\371', (uc)'\372', (uc)'\333', (uc)'\334', (uc)'\335', (uc)'\336', (uc)'\337',
102 (uc)'\340', (uc)'\341', (uc)'\342', (uc)'\343', (uc)'\344', (uc)'\345', (uc)'\346', (uc)'\347',
103 (uc)'\350', (uc)'\351', (uc)'\352', (uc)'\353', (uc)'\354', (uc)'\355', (uc)'\356', (uc)'\357',
104 (uc)'\360', (uc)'\361', (uc)'\362', (uc)'\363', (uc)'\364', (uc)'\365', (uc)'\366', (uc)'\367',
105 (uc)'\370', (uc)'\371', (uc)'\372', (uc)'\373', (uc)'\374', (uc)'\375', (uc)'\376', (uc)'\377',
108 /// Stricmp
110 int Stricmp(const char *str1, const char *str2)
112 unsigned char u1, u2;
114 for(;;)
116 u1 = (unsigned char)*str1++;
117 u2 = (unsigned char)*str2++;
119 if(charmap[u1] != charmap[u2])
120 return charmap[u1] - charmap[u2];
122 if(u1 == '\0')
123 return 0;
128 /// Strnicmp
130 int Strnicmp(const char *str1, const char *str2, int len)
132 unsigned char u1, u2;
134 for(; len != 0; --len)
136 u1 = (unsigned char)*str1++;
137 u2 = (unsigned char)*str2++;
138 if(charmap[u1] != charmap[u2])
139 return charmap[u1] - charmap[u2];
141 if(u1 == '\0')
142 return 0;
145 return 0;
147 #endif
149 /// AllocString
151 /* This allocates a string */
153 char *AllocString(const char *str)
155 char *ptr;
157 if((ptr = malloc(strlen(str) + 1)) == NULL)
158 MemError();
160 strcpy(ptr, str);
162 return ptr;
166 /// Add a string to an already allocated one
168 char *AddString(char *str, const char *astr)
170 char *ptr;
172 if((ptr = malloc(strlen(str) + strlen(astr) + 1)) == NULL)
173 MemError();
175 strcpy(ptr, str);
176 strcat(ptr, astr);
178 free(str);
180 return ptr;
184 /// Convert a string from one charset to another
186 #ifdef AMIGA
187 char *ConvertString(char *str, const char *from_charset, const char *to_charset)
189 char *result = NULL;
190 BOOL fromIsUTF8 = (Stricmp(from_charset, "UTF-8") == 0 || Stricmp(from_charset, "UTF8") == 0);
191 BOOL toIsUTF8 = (Stricmp(to_charset, "UTF-8") == 0 || Stricmp(to_charset, "UTF8") == 0);
193 if(fromIsUTF8 == TRUE && toIsUTF8 == TRUE)
195 // no need to convert from UTF8 to UTF8
196 // just return a plain copy of the string
197 result = strdup(str);
199 else
201 struct codeset *dstCodeset;
203 dstCodeset = CodesetsFind((STRPTR)to_charset,
204 CSA_FallbackToDefault, FALSE,
205 TAG_DONE);
206 if(dstCodeset != NULL)
208 ULONG dstLen = 0;
209 char *dstText = NULL;
211 if(fromIsUTF8 == TRUE)
213 dstText = CodesetsUTF8ToStr(CSA_Source, str,
214 CSA_DestCodeset, dstCodeset,
215 CSA_DestLenPtr, &dstLen,
216 TAG_DONE);
218 else
220 struct codeset *srcCodeset;
222 srcCodeset = CodesetsFind((STRPTR)from_charset,
223 CSA_FallbackToDefault, FALSE,
224 TAG_DONE);
226 if(srcCodeset != NULL)
228 dstText = CodesetsConvertStr(CSA_Source, str,
229 CSA_SourceCodeset, srcCodeset,
230 CSA_DestCodeset, dstCodeset,
231 CSA_DestLenPtr, &dstLen,
232 TAG_DONE);
236 if(dstText != NULL && dstLen != 0)
238 char *buf;
240 // copy the converted string into a separate allocated string
241 if((buf = malloc(dstLen+1)) != NULL)
243 memcpy(buf, dstText, dstLen);
244 buf[dstLen] = '\0';
245 result = buf;
248 CodesetsFreeA(dstText, NULL);
253 return result;
255 #else
256 char *ConvertString(char *str, const char *from_charset, const char *to_charset)
258 char *result = NULL;
259 iconv_t ict;
261 if((ict = iconv_open(to_charset, from_charset)) != (iconv_t)-1)
263 size_t inleft = strlen(str);
264 char *buf;
266 if((buf = malloc((inleft+1)*sizeof(char))) != NULL)
268 size_t outleft = inleft;
269 char *outbuf = buf;
271 if(iconv(ict, &str, &inleft, &outbuf, &outleft) != (size_t)-1)
273 *outbuf = '\0';
274 result = buf;
276 else
278 printf("ERROR: iconv()\n");
279 free(buf);
282 else
283 MemError();
285 iconv_close(ict);
287 else
288 printf("ERROR: iconv_open()\n");
290 return result;
292 #endif
295 /// Add catalog chunk
297 /* This adds a new catalog chunk to the list of catalog
298 chunks. */
300 char *AddCatalogChunk(char *ID, const char *string)
302 struct CatalogChunk *cc, **ccptr;
304 if((cc = malloc(sizeof(*cc))) == NULL)
305 MemError();
307 cc->Next = NULL;
308 cc->ID = *((ULONG *)ID);
309 cc->ChunkStr = AllocString(string);
311 /* Put the new chunk at the end of the chunk list. */
313 for(ccptr = &FirstChunk; *ccptr != NULL; ccptr = &(*ccptr)->Next)
316 *ccptr = cc;
318 return cc->ChunkStr;
322 /// gethex
324 /* This translates an hex character. */
326 int gethex(int c)
328 if(c >= '0' && c <= '9')
329 return c - '0';
330 else if(c >= 'a' && c <= 'f')
331 return c - 'a' + 10;
332 else if(c >= 'A' && c <= 'F')
333 return c - 'A' + 10;
335 ShowError(MSG_ERR_EXPECTEDHEX);
337 return 0;
341 /// getoctal
343 /* This translates an octal digit. */
345 int getoctal(int c)
347 if(c >= '0' && c <= '7')
348 return c - '0';
350 ShowError(MSG_ERR_EXPECTEDOCTAL);
352 return 0;
356 /// ReadLine
358 /* Reading a line is somewhat complicated in order to allow lines of any
359 length.
361 Inputs: fp - the file, where the input comes from
362 AllowComment - TRUE if a leading semicolon should force the
363 line to be interpreted as a comment */
365 char *ReadLine(FILE *fp, UNUSED int AllowComment)
367 char *NewLine = NULL;
368 int c = '\0';
369 int Len = 0, LineLen = 0;
370 int FirstChar = TRUE;
371 int BackslashSeen = FALSE;
372 int BackslashSeenOn = 0; /* Position where the last backslash was seen. */
373 int CommentLine = FALSE; /* If TRUE, we should ignore any trailing \'s */
375 while(c != EOF)
377 if(Len + 10 > LineLen)
379 NewLine = realloc(NewLine, LineLen + BUFSIZE);
380 LineLen += BUFSIZE;
383 c = getc(fp);
385 if(FirstChar)
387 if(c == EOF)
389 if(NewLine != NULL)
390 free(NewLine);
391 return NULL;
394 if(c == ';')
396 CommentLine = TRUE;
399 FirstChar = FALSE;
402 switch(c)
404 case '\r':
405 break;
407 case '\n':
408 ++ScanLine;
409 if(BackslashSeen)
411 NewLine[Len++] = c;
412 BackslashSeen = FALSE;
413 break;
415 c = EOF;
417 case EOF:
418 break;
420 /* Check for trailing \\ */
421 case '\\':
423 if(!CommentLine)
425 if(BackslashSeen)
427 if(BackslashSeenOn ==(Len - 1))
429 BackslashSeen = FALSE;
430 NewLine[Len++] = c;
431 break;
435 BackslashSeen = TRUE;
436 BackslashSeenOn = Len;
439 NewLine[Len++] = c;
440 break;
444 default:
445 BackslashSeen = FALSE;
446 NewLine[Len++] = c;
450 NewLine[Len] = '\0';
452 return NewLine;
457 /// OverSpace
459 /* This removes trailing blanks. */
461 void OverSpace(char **strptr)
463 int c;
465 while((c = **strptr) == ' ' || c == '\t')
467 (*strptr)++;
472 /// Expunge
474 void Expunge(void)
476 #ifdef AMIGA
477 if(DoExpunge)
479 struct Library *localeBase;
481 // this may look utterly wrong since we are trying to RemLibrary() a library
482 // which we just opened. But this is the most convenient way to invoke the
483 // Expunge() function of locale.library, which will just remove any still
484 // opened but unused catalog file from memory without removing locale.library
485 // itself.
486 if((localeBase = OpenLibrary( "locale.library", 0)) != NULL)
488 RemLibrary(localeBase);
489 CloseLibrary(localeBase);
492 #endif // AMIGA
496 /// ReadChar
498 /* ReadChar scans an input line and translates the backslash characters.
499 Inputs: char * - a pointer to a string pointer; the latter points
500 to the next character to be read and points behind
501 the read bytes after executing ReadChar
502 dest - a pointer to a buffer, where the read bytes should be
503 stored
504 Result: number of bytes that are written to dest (between 0 and 2) */
506 int ReadChar(char **strptr, char *dest)
508 char c;
509 int i;
511 switch(c = *((*strptr)++))
513 case '\\':
515 switch(c = tolower((int)*((*strptr)++)))
517 case '\n':
518 return(0);
519 break;
521 case 'b':
522 *dest = '\b';
523 break;
525 case 'c':
526 *dest = '\233';
527 break;
529 case 'e':
530 *dest = '\033';
531 break;
533 case 'f':
534 *dest = '\f';
535 break;
537 case 'g':
538 *dest = '\007';
539 break;
541 case 'n':
542 *dest = '\n';
543 break;
545 case 'r':
546 *dest = '\r';
547 break;
549 case 't':
550 *dest = '\t';
551 break;
553 case 'v':
554 *dest = '\013';
555 break;
557 case 'x':
559 *dest = gethex((int)**strptr);
560 (*strptr)++;
561 c = **strptr;
562 if((c >= '0' && c <= '9') ||
563 (c >= 'a' && c <= 'f') ||
564 (c >= 'A' && c <= 'F'))
566 *dest =(*dest << 4) + gethex((int)c);
567 (*strptr)++;
570 break;
572 case '0':
573 case '1':
574 case '2':
575 case '3':
576 case '4':
577 case '5':
578 case '6':
579 case '7':
581 *dest = getoctal((int)c);
583 for(i = 0; i < 2; i++)
585 c = **strptr;
586 if(c >= '0' && c <= '7')
588 *dest =(*dest << 3) + getoctal((int)c);
589 (*strptr)++;
593 break;
595 case ')':
596 case '\\':
597 *(dest++) = '\\';
598 *dest = c;
599 return(2);
600 break;
602 default:
603 *dest = c;
604 break;
607 break;
609 default:
611 *dest = c;
613 break;
615 return(1);
619 /// AllocFileName
621 /* This function creates a copy of a filename, and optionally
622 removes an ending and pathname components, if desired.
623 Inputs: filename - the filename to copy
624 howto - a set of bits
625 bit 0: 1 = remove ending, 0 = leave it
626 bit 1: 1 = remove pathname, 0 = leave it
627 Result: the copy of the filename
630 char *AllocFileName(char *filename, int howto)
632 char *tempstr, *ptr;
634 if((tempstr = strdup(filename)) == NULL)
636 MemError();
637 MyExit(10);
640 /* Remove pathname components, if desired. */
642 if(howto & 2)
644 if((ptr = strchr(tempstr, ':')) != NULL)
646 tempstr = ptr + 1;
648 if((ptr = strrchr(tempstr, '/')) != NULL)
650 tempstr = ptr + 1;
654 /* Remove ending, if desired. */
656 if(howto & 1)
658 if((ptr = strrchr(tempstr, '.')) != NULL)
660 *ptr = '\0';
664 return(tempstr);
668 /// AddFileName
670 /* This function adds a pathname and a filename to a full
671 filename.
672 Inputs: pathname - the leading pathname
673 filename - the filename
674 Result: The new filename */
676 char *AddFileName(char *pathname, char *filename)
678 char *buffer;
680 #ifdef AMIGA
681 int size = strlen(pathname) + strlen(filename) + 2;
683 if((buffer = malloc(size)) == NULL)
685 MemError();
686 MyExit(10);
689 strcpy(buffer, pathname);
690 AddPart((char *)buffer, (char *)filename, size);
691 #else
692 if(asprintf(&buffer, "%s/%s", pathname, filename) < 0)
694 MemError();
695 MyExit(10);
697 #endif
699 return buffer;
703 /// Usage
705 /* The Usage function describes the program's calling syntax. */
707 void Usage(void)
709 fprintf(stderr, "%s\n", VString);
710 fprintf(stderr, "%s\n", EString);
711 fprintf(stderr,
712 "\n" \
713 "%s\n" \
714 " FlexCat CDFILE/A,CTFILE,POFILE,CATALOG/K,NEWCTFILE/K,SOURCES/M,\n" \
715 " WARNCTGAPS/S,NOOPTIM/S,FILL/S,FLUSH/S,NOBEEP/S,\n" \
716 " QUIET/S,NOLANGTOLOWER/S,NOBUFFEREDIO/S,MODIFIED/S,\n" \
717 " CODESET/K,COPYMSGNEW/S,OLDMSGNEW/K\n" \
718 "\n", MSG_USAGE_HEAD);
719 fprintf(stderr, "%s\n", MSG_USAGE);
720 MyExit(5);
724 /// wbmain
726 /* Dice's entry point for workbench programs */
728 #if defined(AMIGA) && defined(_DCC)
729 void wbmain(struct WBStartup *wbmsg)
731 fprintf(stderr, "FlexCat can't be run from Workbench!\n\n");
732 fprintf(stderr, "Open a shell session and type FlexCat\n");
733 fprintf(stderr, "for syntax and more information.\n");
735 exit(5);
737 #endif