]> 4ch.mooo.com Git - 16.git/blob - src/lib/nyan/kitten.c
49cb74744e3c8e7e26d378be080786a57cccc5c2
[16.git] / src / lib / nyan / kitten.c
1 \r
2 /* Functions that emulate UNIX catgets */\r
3 \r
4 /* Copyright (C) 1999,2000,2001 Jim Hall <jhall@freedos.org> */\r
5 \r
6 /*\r
7   This library is free software; you can redistribute it and/or\r
8   modify it under the terms of the GNU Lesser General Public\r
9   License as published by the Free Software Foundation; either\r
10   version 2.1 of the License, or (at your option) any later version.\r
11 \r
12   This library is distributed in the hope that it will be useful,\r
13   but WITHOUT ANY WARRANTY; without even the implied warranty of\r
14   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU\r
15   Lesser General Public License for more details.\r
16 \r
17   You should have received a copy of the GNU Lesser General Public\r
18   License along with this library; if not, write to the Free Software\r
19   Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA\r
20 */\r
21 \r
22 #include <stdio.h>                      /* sprintf */\r
23 #include <stdlib.h>                     /* getenv  */\r
24 #include <string.h>                     /* strchr */\r
25 #include <fcntl.h>\r
26 \r
27 #include "src/lib/nyan/kitten.h"\r
28 \r
29 \r
30 /* DB stuff */\r
31 \r
32 struct db_list{\r
33   struct db_list *next;\r
34   char *key;\r
35   char *value;\r
36 };\r
37 \r
38 typedef struct db_list db_t;\r
39 \r
40 db_t *db_insert (char *key, char *value);\r
41 db_t *db_fetch (char *key);\r
42 \r
43 \r
44 /* External functions */\r
45 \r
46 int get_line (int file, char *buffer, int size);\r
47 \r
48 \r
49 /* Local prototypes */\r
50 \r
51 int catread (char *catfile);            /* Reads a catfile into the hash */\r
52 char *processEscChars(char *line);  /* Converts c escape sequences to chars */\r
53 \r
54 /* Globals */\r
55 \r
56 nl_catd _kitten_catalog = 0;                    /* _kitten_catalog descriptor, either 0 or 1 */\r
57 \r
58 \r
59 \r
60 #if defined(__SMALL__)                                                  /* it's not very portable ;) */\r
61 \r
62 #include <dos.h>\r
63 /* assert we are running in small model */\r
64 /* else pointer below has to be done correctly */\r
65 char verify_small_pointers[sizeof(void*) == 2 ? 1 : -1];\r
66 \r
67 \r
68 int dos_open(char *filename, int mode);\r
69 #define open(filename,mode) dos_open(filename,mode)\r
70 \r
71 int dos_read(int file, void *ptr, unsigned count);\r
72 #define read(file, ptr, count) dos_read(file,ptr,count)\r
73 \r
74 void dos_close(int file);\r
75 #define close(file) dos_close(file)\r
76 \r
77 #ifdef __WATCOMC__\r
78 \r
79 #pragma aux dos_open = \\r
80 "mov ax, 0x3d00" \\r
81 "int 0x21" \\r
82 "jnc noerror" \\r
83 "mov ax, 0xffff" \\r
84 "noerror:" \\r
85 parm [dx] [ax] value [ax];\r
86 \r
87 #pragma aux dos_read = \\r
88 "mov ah, 0x3f" \\r
89 "int 0x21" \\r
90 "jnc noerror" \\r
91 "xor ax, ax" \\r
92 "noerror:" \\r
93 parm [bx] [dx] [cx] value [ax];\r
94 \r
95 #pragma aux dos_close = \\r
96 "mov ah, 0x3e" \\r
97 "int 0x21" \\r
98 parm [bx];\r
99 \r
100 #else\r
101 \r
102 int dos_open(char *filename, int mode)\r
103 {\r
104   union REGS r;\r
105 \r
106   if (mode);                                    /* mode ignored - readonly supported */\r
107 \r
108   r.h.ah = 0x3d;\r
109   r.h.al = 0;                                   /* read mode only supoported now !! */\r
110   r.x.dx = (unsigned)filename;\r
111   intdos(&r,&r);\r
112 \r
113   if (r.x.cflag)\r
114     return -1;\r
115   return r.x.ax;\r
116 }\r
117 \r
118 int dos_read(int file, void *ptr, unsigned count)\r
119 {\r
120   union REGS r;\r
121 \r
122   r.h.ah = 0x3f;\r
123   r.x.bx = file;\r
124   r.x.cx = count;\r
125   r.x.dx = (unsigned)ptr;\r
126   intdos(&r,&r);\r
127 \r
128   if (r.x.cflag)\r
129     return 0;\r
130   return r.x.ax;\r
131 }\r
132 \r
133 void dos_close(int file)\r
134 {\r
135   union REGS r;\r
136 \r
137   r.h.ah = 0x3e;\r
138   r.x.bx = file;\r
139   intdos(&r,&r);\r
140 }\r
141 \r
142 #endif\r
143 #endif\r
144 \r
145 #ifndef NOCATS\r
146 \r
147 /* Functions */\r
148 \r
149 char *\r
150 pascal kittengets(int set_number, int message_number, char *message)\r
151 {\r
152   /* get message from a message _kitten_catalog */\r
153 \r
154   /* 'message' should really be const, but not when it is returned */\r
155 \r
156   /* On success, catgets() returns a pointer to an internal buffer\r
157      area containing the null-terminated message string.  On failure,\r
158      catgets() returns the value 'message'.  */\r
159 \r
160   char key[10];\r
161   db_t *ptr;\r
162 \r
163 \r
164   /* fetch the message that goes with the set/message number */\r
165 \r
166   sprintf (key, "%d.%d", set_number, message_number);\r
167   ptr = db_fetch (key);\r
168 \r
169   /* printf("\ncatgets %s\n",message); */\r
170 \r
171   if (ptr)\r
172     {\r
173       /*     printf("------> %s\n",ptr->value); */\r
174 \r
175       return (ptr->value);\r
176     }\r
177 \r
178   /* else */\r
179 \r
180   return (message);\r
181 }\r
182 \r
183 nl_catd\r
184 kittenopen(char *name)\r
185 {\r
186   /* catopen() returns a message _kitten_catalog descriptor of type nl_catd on\r
187      success.  On failure, it returns -1. */\r
188 \r
189   /* 'flag' is completely ignored here. */\r
190 \r
191   char catfile[256];                    /* full path to the msg _kitten_catalog */\r
192   char *nlsptr;                         /* ptr to NLSPATH */\r
193   char *lang;                   /* ptr to LANG */\r
194 \r
195 \r
196 \r
197   /* Open the _kitten_catalog file */\r
198 \r
199   /* The value of `_kitten_catalog' will be set based on catread */\r
200 \r
201   if (_kitten_catalog)\r
202     {\r
203       /* Already one open */\r
204 \r
205       printf("cat already open\n");\r
206       return (-1);\r
207     }\r
208 \r
209   /* If the message _kitten_catalog file name contains a directory separator,\r
210      assume that this is a real path to the _kitten_catalog file.  Note that\r
211      catread will return a true or false value based on its ability\r
212      to read the catfile. */\r
213 \r
214   if (strchr (name, '\\'))\r
215     {\r
216       /* first approximation: 'name' is a filename */\r
217 \r
218       printf("found \\\n");\r
219 \r
220       _kitten_catalog = catread (name);\r
221       return (_kitten_catalog);\r
222     }\r
223 \r
224   /* If the message _kitten_catalog file name does not contain a directory\r
225      separator, then we need to try to locate the message _kitten_catalog on\r
226      our own.  We will use several methods to find it. */\r
227 \r
228   /* We will need the value of LANG, and may need a 2-letter abbrev of\r
229      LANG later on, so get it now. */\r
230 \r
231   lang = getenv ("LANG");\r
232 \r
233   if (lang == NULL)\r
234     {\r
235       /* printf("no lang= found\n"); */\r
236 \r
237       /* Return failure - we won't be able to locate the cat file */\r
238       return (-1);\r
239     }\r
240 \r
241 \r
242   /* step through NLSPATH */\r
243 \r
244   nlsptr = getenv ("NLSPATH");\r
245 \r
246 \r
247   if (nlsptr == NULL)\r
248     {\r
249       /* printf("no NLSPATH= found\n"); */\r
250 \r
251       /* Return failure - we won't be able to locate the cat file */\r
252       return (-1);\r
253     }\r
254 \r
255       /* printf("nlsptr:%s\n",nlsptr); */\r
256 \r
257   while (*nlsptr)\r
258     {\r
259       char *tok = strchr(nlsptr, ';');\r
260       int toklen;\r
261 \r
262 \r
263       if (tok == NULL) tok = nlsptr + strlen(nlsptr);\r
264       toklen=tok-nlsptr;\r
265       /* Try to find the _kitten_catalog file in each path from NLSPATH */\r
266 \r
267       /* Rule #1: %NLSPATH%\%LANG%\cat */\r
268 \r
269       memcpy(catfile,nlsptr,toklen);\r
270       sprintf(catfile+toklen,"\\%s\\%s",lang,name);\r
271 \r
272       _kitten_catalog = catread (catfile);\r
273       if (_kitten_catalog)\r
274         {\r
275           return (_kitten_catalog);\r
276         }\r
277 \r
278       /* Rule #2: %NLSPATH%\cat.%LANG% */\r
279 \r
280       sprintf(catfile+toklen,"\\%s.%s",name, lang);\r
281 \r
282       _kitten_catalog = catread (catfile);\r
283 \r
284       if (_kitten_catalog)\r
285         {\r
286           return (_kitten_catalog);\r
287         }\r
288 \r
289       /* Rule #3: if LANG looks to be in format "en-UK" then\r
290          %NLSPATH%\cat.EN */\r
291 \r
292       if (lang[2] == '-')\r
293         {\r
294           lang[2] = 0;\r
295           sprintf(catfile+toklen,"\\%s.%s",name,lang);\r
296           lang[2] = '-';\r
297 \r
298           _kitten_catalog = catread (catfile);\r
299           if (_kitten_catalog)\r
300             {\r
301               return (_kitten_catalog);\r
302             }\r
303         }\r
304 \r
305       /* Grab next tok for the next while iteration */\r
306       nlsptr = tok;\r
307       if (*nlsptr) nlsptr++;\r
308 \r
309     } /* while tok */\r
310 \r
311   /* We could not find it.  Return failure. */\r
312 \r
313   return (0);\r
314 }\r
315 \r
316 int\r
317 catread (char *catfile)\r
318 {\r
319   int   file;                           /* pointer to the catfile */\r
320   char *key;                            /* part of key-value for hash */\r
321   char *value;                          /* part of key-value for hash */\r
322   char inBuffer[256];                   /* the string read from the file */\r
323 \r
324   /* Open the catfile for reading */\r
325 \r
326   /*printf("catread %s\n",catfile); */\r
327 \r
328   file = open (catfile, O_RDONLY | O_TEXT);\r
329   if (file < 0)\r
330     {\r
331       /* Cannot open the file.  Return failure */\r
332           /* printf("catread: cant read %s\n",catfile); */\r
333       return (0);\r
334     }\r
335 \r
336   /*printf("catread %s success\n",catfile);*/\r
337 \r
338   /* Read the file into memory */\r
339 \r
340   while (get_line (file, inBuffer, sizeof(inBuffer)))\r
341     {\r
342       /* Break into parts.  Entries should be of the form:\r
343          "1.2:This is a message" */\r
344 \r
345       /* A line that starts with '#' is considered a comment, and will\r
346          be thrown away without reading it. */\r
347 \r
348       if (inBuffer[0] == '#')           /* comment */\r
349         continue;\r
350 \r
351       if ((key = strchr (inBuffer, ':')) != NULL)\r
352         {\r
353           *key = 0;\r
354 \r
355           value = processEscChars(key+1);\r
356 \r
357           db_insert (inBuffer, value);\r
358         }\r
359 \r
360     } /* while */\r
361 \r
362   close (file);\r
363 \r
364   /* Return success */\r
365 \r
366   return (1);\r
367 }\r
368 \r
369 void\r
370 kittenclose (void)\r
371 {\r
372   /* close a message _kitten_catalog */\r
373 \r
374   _kitten_catalog = 0;\r
375 }\r
376 \r
377 \r
378 \r
379 /**\r
380  * Process strings, converting \n, \t, \v, \b, \r, \f, \\, \ddd, \xdd and \x0dd\r
381  * to actual chars. (Note: \x is an extension to support hexadecimal)\r
382  * This method is used to allow the message _kitten_catalog to use c escape sequences.\r
383  * Modifies the line in-place (always same size or shorter).\r
384  * Returns a pointer to input string.\r
385  */\r
386 \r
387 int mystrtoul(char *src, int base, int size, int *error)\r
388 {\r
389   int ret = 0;\r
390 \r
391   *error = 1;\r
392 \r
393   for (; size > 0; size--)\r
394     {\r
395       int digit;\r
396       int ch = *src++;\r
397 \r
398       if (ch >= '0' && ch <= '9') digit = ch - '0';\r
399       else if (ch >= 'A' && ch <= 'Z') digit = ch - 'A' + 10;\r
400       else if (ch >= 'a' && ch <= 'z') digit = ch - 'a' + 10;\r
401       else\r
402         {\r
403           return 0;\r
404         }\r
405 \r
406       if (digit >= base)\r
407         {\r
408           return 0;\r
409         }\r
410 \r
411       ret = ret * base + digit;\r
412     }\r
413 \r
414   *error = 0;\r
415 \r
416   return ret;\r
417 }\r
418 \r
419 \r
420 char *processEscChars(char *line)\r
421 {\r
422   register char *src = line, *dst = line;\r
423 \r
424   /* used when converting \xdd and \ddd (hex or octal) characters */\r
425   char ch;\r
426 \r
427   if (line == NULL) return NULL;\r
428 \r
429   /* cycle through copying characters, except when a \ is encountered. */\r
430   for ( ; *src != '\0'; src++, dst++)\r
431     {\r
432       ch = *src;\r
433 \r
434       if (ch == '\\')\r
435         {\r
436           src++; /* point to char following slash */\r
437           switch (ch = *src)\r
438             {\r
439             case '\\': /* a single slash */\r
440               ch = '\\';\r
441               break;\r
442             case 'n': /* a newline (linefeed) */\r
443               ch = '\n';\r
444               break;\r
445             case 'r': /* a carriage return */\r
446               ch = '\r';\r
447               break;\r
448             case 't': /* a horizontal tab */\r
449               ch = '\t';\r
450               break;\r
451             case 'v': /* a vertical tab */\r
452               ch = '\v';\r
453               break;\r
454             case 'b': /* a backspace */\r
455               ch = '\b';\r
456               break;\r
457             case 'a': /* alert */\r
458               ch = '\a';\r
459               break;\r
460             case 'f': /* formfeed */\r
461               ch = '\f';\r
462               break;\r
463             case 'x': /* extension supporting hex numbers \xdd or \x0dd */\r
464               {\r
465                 int error;\r
466                 ch  = mystrtoul(src+1,16,2, &error); /* get value */\r
467                 if (!error) /* store character */\r
468                   {\r
469                     src += 2;\r
470                   }\r
471                 else /* error so just store x (loose slash) */\r
472                   {\r
473                     ch = *src;\r
474                   }\r
475               }\r
476 \r
477               break;\r
478             default: /* just store letter (loose slash) or handle octal */\r
479 \r
480               {\r
481                 int error;\r
482                 ch  = mystrtoul(src,8,3, &error); /* get value */\r
483                 if (!error) /* store character */\r
484                   {\r
485                     src += 3;\r
486                   }\r
487                 else\r
488                   ch = *src;\r
489               }\r
490 \r
491               break;\r
492             }\r
493         }\r
494 \r
495       *dst = ch;\r
496     }\r
497 \r
498   /* ensure '\0' terminated */\r
499   *dst = '\0';\r
500 \r
501   return line;\r
502 }\r
503 \r
504 \r
505 \r
506 int\r
507 get_line (int file, char *str, int size)\r
508 {\r
509   int success = 0;\r
510 \r
511   /* now, read the string */\r
512 \r
513   for ( ; size > 0; )\r
514     {\r
515       if (read(file,str,1) <= 0)\r
516         break;\r
517 \r
518       success = 1;\r
519 \r
520       if (*str == '\r')\r
521         continue;\r
522 \r
523       if (*str == '\n')\r
524         break;\r
525 \r
526       str++;\r
527       size--;\r
528 \r
529     } /* while */\r
530 \r
531   *str = 0;\r
532 \r
533   return success;\r
534 }\r
535 \r
536 /* Function prototypes */\r
537 \r
538 \r
539 /* Global variables */\r
540 \r
541 static db_t *hashtab[1];\r
542 \r
543 \r
544 /* Functions */\r
545 \r
546 \r
547 /* db_fetch() - Query the hash and return a struct that contains the\r
548    key and the pointer.  The calling function should not look beyond\r
549    that. */\r
550 \r
551 db_t *\r
552 db_fetch (char *s)\r
553 {\r
554   db_t *db_ptr;\r
555 \r
556   for (db_ptr = hashtab[0]; db_ptr != NULL; db_ptr = db_ptr->next)\r
557     {\r
558       if (strcmp (s, db_ptr->key) == 0)\r
559         {\r
560           break;\r
561 \r
562         }\r
563     }\r
564 \r
565 \r
566   return (db_ptr);\r
567 }\r
568 \r
569 /* db_insert() - Inserts a key,value pair into the hash.  If the key\r
570    already exists in the hash, the new value is NOT inserted. */\r
571 \r
572 db_t *\r
573 db_insert (char *key, char *value)\r
574 {\r
575   db_t *db_ptr;\r
576 \r
577   if ((db_ptr = db_fetch (key)) == NULL)\r
578     {\r
579       /* not found */\r
580 \r
581       db_ptr = (db_t *) malloc (sizeof (*db_ptr));\r
582 \r
583       if (db_ptr == NULL || (db_ptr->key = strdup (key)) == NULL)\r
584         {\r
585           return (NULL);\r
586         }\r
587 \r
588       /* insert the key,value into the hash. */\r
589 \r
590       db_ptr->next = hashtab[0];\r
591       hashtab[0] = db_ptr;\r
592     }\r
593 \r
594   else\r
595     {\r
596       /* already there */\r
597 \r
598       free ((void *) db_ptr->value);\r
599     }\r
600 \r
601   if ((db_ptr ->value = strdup (value)) == NULL)\r
602     {\r
603       return (NULL);\r
604     }\r
605 \r
606   /* else */\r
607 \r
608   return (db_ptr);\r
609 }\r
610 \r
611 \r
612 \r
613 #endif /* NOCATS */\r