(U-000233B6): New character.
[chise/xemacs-chise.git] / lib-src / make-po.c
1 /* Generate .po file from doc-string file.
2
3    Scan specified doc-string file, creating .po format messages for processing
4    with msgfmt.  The results go to standard output or to a file specified
5    with -a or -o (-a to append, -o to start from nothing).
6
7    Kludge to make up for shortcoming in make-docfile and Snarf-documentation:
8    If arg before input filename is -p, we are scanning an add-on
9    package, which requires slightly different processing.
10 */
11
12 #include <stdio.h>
13 #include <stdlib.h>
14
15 #ifndef EXIT_SUCCESS
16 #define EXIT_SUCCESS 0
17 #define EXIT_FAILURE 1
18 #endif
19
20 /* #define BUFSIZE    8192 */
21 /* #define BUFSIZE    16384 */
22 #define BUFSIZE    32768
23 #define NEWSTRING  31      /* Character signalling start of new doc string */
24 #define LINEEND    "\\n"
25 #define ENDSTRING  "\"\n"
26 #define LINEBEGIN  "       \""
27 #define LINEBREAK  ENDSTRING LINEBEGIN
28
29 /* some brain-dead headers define this ... */
30 #undef FALSE
31 #undef TRUE
32 enum boolean { FALSE, TRUE };
33
34
35 /***********************/
36 /* buffer pseudo-class */
37 /***********************/
38
39 typedef struct _buffer
40 {
41   size_t index;  /* current position in buf[] */
42   size_t size;   /* size of buf */
43   char *buf;
44 } buffer_struct;
45
46 #define BUF_NULL  {0, 0, NULL}
47
48 int  buf_init  (buffer_struct *buffer, size_t size);
49 void buf_free  (buffer_struct *buffer);
50 void buf_clear (buffer_struct *buffer);
51 int  buf_putc  (buffer_struct *buffer, int c);
52 int  buf_print (buffer_struct *buffer, const char *s);
53
54
55 /********************/
56 /* global variables */
57 /********************/
58
59 FILE *infile  = NULL;
60 FILE *outfile = NULL;
61 buffer_struct buf = BUF_NULL;
62
63
64 void scan_file (enum boolean package);
65 void initialize (void);
66 void clean_exit (int status);
67 void buf_putc_safe (int c);
68 void buf_print_safe (const char *s);
69 void terminate_string (void);
70
71 main (int argc, char *argv[])
72 {
73   register int i;
74   enum boolean package = FALSE;  /* TRUE if scanning add-on package */
75
76   initialize ();
77
78   outfile = stdout;
79
80   /* If first two args are -o FILE, output to FILE. */
81   i = 1;
82   if (argc > i + 1 && strcmp (argv[i], "-o") == 0) {
83     outfile = fopen (argv[++i], "w");
84     ++i;
85   }
86   /* ...Or if args are -a FILE, append to FILE. */
87   if (argc > i + 1 && strcmp (argv[i], "-a") == 0) {
88     outfile = fopen (argv[++i], "a");
89     ++i;
90   }
91   if (!outfile) {
92     fprintf (stderr, "Unable to open output file %s\n", argv[--i]);
93     return 1;
94   }
95
96   if (argc > i && !strcmp (argv[i], "-p")) {
97     package = TRUE;
98     ++i;
99   }
100
101   infile = fopen (argv[i], "r");
102   if (!infile) {
103     fprintf (stderr, "Unable to open input file %s\n", argv[i]);
104     return 1;
105   }
106
107   scan_file (package);
108   clean_exit (EXIT_SUCCESS);
109 }
110
111
112 void scan_file (enum boolean package)
113 {
114   register int c;   /* Character read in */
115
116   fprintf (outfile, "###############\n");
117   fprintf (outfile, "# DOC strings #\n");
118   fprintf (outfile, "###############\n");
119
120   while (c = getc (infile), !feof (infile)) {
121     if (c == NEWSTRING) {
122       /* If a string was being processed, terminate it. */
123       if (buf.index > 0)
124         terminate_string ();
125
126       /* Skip function or variable name. */
127       while (c != '\n')
128         c = getc (infile);
129       c = getc (infile);
130
131       /* Begin a new string. */
132       fprintf (outfile, "msgid  \"");
133       buf_print_safe ("msgstr \"");
134     }
135
136     if (c == '\n') {
137       /* Peek at next character. */
138       c = getc (infile);
139       ungetc (c, infile);
140
141       /* For add-on (i.e., non-preloaded) documentation, ignore the last
142          carriage return of a string. */
143       if (!(package && c == NEWSTRING)) {
144         fprintf (outfile, LINEEND);
145         buf_print_safe (LINEEND);
146       }
147
148       /* If not end of string, continue it on the next line. */
149       if (c != NEWSTRING) {
150         fprintf (outfile, LINEBREAK);
151         buf_print_safe (LINEBREAK);
152       }
153     }
154     else {
155
156       /* If character is \ or ", precede it by a backslash. */
157       if (c == '\\' || c == '\"') {
158         putc ('\\', outfile);
159         buf_putc_safe ('\\');
160       }
161
162       putc (c, outfile);
163       buf_putc_safe (c);
164     }
165   }
166   terminate_string ();
167 }
168
169
170 /* initialize sets up the global variables.
171 */
172 void initialize (void)
173 {
174   if (buf_init (&buf, BUFSIZE) != 0)
175     clean_exit (EXIT_FAILURE);
176 }
177
178
179 /* clean_exit returns any resources and terminates the program.
180    An error message is printed if status is EXIT_FAILURE.
181 */
182 void clean_exit (int status)
183 {
184   if (buf.size > 0)
185     buf_free (&buf);
186   if (outfile)
187     fclose (outfile);
188   if (infile)
189     fclose (infile);
190
191   if (status == EXIT_FAILURE)
192     fprintf (stderr, "make-po abnormally terminated\n");
193   exit (status);
194 }
195
196
197 /* buf_putc_safe writes the character c on the global buffer buf,
198    checking to make sure that the operation was successful.
199 */
200 void buf_putc_safe (int c)
201 {
202   register int status;
203   
204   status = buf_putc (&buf, c);
205   if (status == EOF)
206     clean_exit (EXIT_FAILURE);
207 }
208
209
210 /* buf_putc_safe writes the string s on the global buffer buf,
211    checking to make sure that the operation was successful.
212 */
213 void buf_print_safe (const char *s)
214 {
215   register int status;
216
217   status = buf_print (&buf, s);
218   if (status < 0)
219     clean_exit (EXIT_FAILURE);
220 }
221
222
223 /* terminate_string terminates the current doc string and outputs the buffer.
224 */
225 void terminate_string (void)
226   {
227     fprintf (outfile, ENDSTRING);
228
229     /* Make the "translation" different from the original string. */
230     buf_print_safe ("_X");
231
232     buf_print_safe (ENDSTRING);
233     fprintf (outfile, "%s", buf.buf);
234     buf_clear (&buf);
235   }
236
237
238 /*********************************/
239 /* buffer pseudo-class functions */
240 /*********************************/
241
242 /* buf_init initializes a buffer to the specified size.
243    It returns non-zero if the attempt fails.
244 */
245 int buf_init (buffer_struct *buffer, size_t size)
246 {
247   buffer->buf = malloc (size);
248   if (buffer->buf == NULL)
249     return 1;
250
251   buffer->size = size;
252   buf_clear (buffer);
253   return 0;
254 }
255
256
257 /* buf_free releases the memory allocated for the buffer.
258 */
259 void buf_free (buffer_struct *buffer)
260 {
261   free (buffer->buf);
262   buffer->size = 0;
263 }
264
265
266 /* buf_clear resets a buffer to an empty string.
267 */
268 void buf_clear (buffer_struct *buffer)
269 {
270   buffer->index = 0;
271   buffer->buf[0] = '\0';
272 }
273
274
275 /* buf_putc writes the character c on the buffer.
276    It returns the character written, or EOF for error.
277 */
278 int buf_putc (buffer_struct *buffer, int c)
279 {
280   if (buffer->index >= buffer->size)
281     return EOF;
282
283   buffer->buf[buffer->index++] = c;
284   return c;
285 }
286
287
288 /* buf_print writes the string s on the buffer.
289    It returns the number of characters written, or negative if an error occurred.
290 */
291 int buf_print (buffer_struct *buffer, const char *s)
292 {
293   register int len;
294
295   len = strlen (s);
296   if (buffer->index + len >= buffer->size)
297     return -1;
298
299   sprintf (&(buffer->buf[buffer->index]), s);
300   buffer->index += len;
301   return len;
302 }