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