]> mj.ucw.cz Git - misc.git/blob - unword.c
songsack: update for new libucw
[misc.git] / unword.c
1 /*
2  *      Extract ASCII Text from M$ Word Document
3  *
4  *      (c) 1997 Martin Mares <mj@atrey.karlin.mff.cuni.cz>
5  */
6
7 /* FIXME: endianity dependencies! */
8
9 #include <stdio.h>
10 #include <stdlib.h>
11 #include <string.h>
12
13 FILE *fi, *fo;
14
15 typedef unsigned char byte;
16 typedef unsigned short word;
17 typedef unsigned int ulg;
18
19 byte
20 gb(void)
21 {
22   return fgetc(fi);
23 }
24
25 word
26 gw(void)
27 {
28   word k = fgetc(fi);
29   k = k | (fgetc(fi) << 8);
30   return k;
31 }
32
33 ulg
34 gl(void)
35 {
36   ulg l = gw();
37   l = l | (gw() << 16);
38   return l;
39 }
40
41 void
42 ole(void)
43 {
44   ulg p, word;
45   byte oh[0x80], pn[0x80];
46   int z, x, y;
47
48   fseek(fi, 0x30, SEEK_SET);
49   p = 0x200 * gl() + 0x200;                             /* Position of central directory */
50   word = 0;
51   printf("Central OLE directory at 0x%x\n", p);
52   fseek(fi, p, SEEK_SET);
53   while (fread(oh, sizeof(oh), 1, fi) == 1)
54         {
55           z = y = 0;
56           if ((!oh[0] && !oh[1]) || (!oh[2] && !oh[3]))
57                 break;
58           if (oh[z] < 32 && !oh[z+1])
59                 {
60                   z += 2;
61                   pn[y++] = '>';
62                   pn[y++] = ' ';
63                 }
64           while (oh[z] || oh[z+1])
65                 {
66                   x = oh[z] | (oh[z+1] << 8);
67                   if (x >= 32 && x < 127)
68                         pn[y++] = x;
69                   else
70                         pn[y++] = '?';
71                   z += 2;
72                 }
73           pn[y] = 0;
74           p = 0x200 * (oh[0x74] | (oh[0x75] << 8) | (oh[0x76] << 16) | (oh[0x77] << 24)) + 0x200;
75           printf("%08x %s\n", p, pn);
76           if (!strcmp(pn, "WordDocument"))
77                 word = p;
78         }
79   if (!word)
80         {
81           fprintf(stderr, "Word doc stream not found!\n");
82           exit(1);
83         }
84   fseek(fi, word, SEEK_SET);
85 }
86
87 struct fib {
88   word magic;
89   word vers;
90   word product;
91   word lang;
92   word pnnext;
93   byte flags1;
94   byte flags2;
95   word back;
96   ulg cryptkey;
97   byte environ;
98   byte rfu;
99   word textcharset;                                             /* 0=win, 256=mac */
100   word tablecharset;                                    /* 0=win, 256=mac */
101   ulg firsttextchar;
102   ulg xx[6];
103   ulg textlen;
104   ulg footlen;
105   ulg hdrlen;
106   ulg macrolen;
107   ulg annolen;
108   ulg endnotelen;
109   ulg textboxlen;
110   ulg htextboxlen;
111   ulg rfu2;
112   ulg stshorigp, stshorigl;
113   ulg stshp, stshl;
114   ulg footrefp, footrefl;
115   ulg foottxtp, foottxtl;
116   ulg annorefp, annorefl;
117   ulg annotxtp, annotxtl;
118   ulg sedp, sedl;
119   ulg pardp, pardl;
120   ulg pahp, pahl;
121   ulg glossp, glossl;
122   ulg glosp, glosl;
123   ulg hdrp, hdrl;
124   ulg chpbp, chpbl;
125   ulg papbp, papbl;
126   ulg seap, seal;
127   ulg ffnsp, ffnsl;
128   ulg mainfpp, mainfpl;
129   ulg headfpp, headfpl;
130   ulg footfpp, footfpl;
131   ulg annofpp, annofpl;
132   ulg macfpp, macfpl;
133   ulg boosp, boosl;
134   ulg bookop, bookol;
135   ulg booklp, bookll;
136   ulg cmdsp, cmdsl;
137   ulg mcrpp, mcrpl;
138   ulg mcrsp, mcrsl;
139   ulg pdrvp, pdrvl;
140   ulg prenvpp, prenvpl;
141   ulg prenvlp, prenvll;
142   ulg wssp, wssl;
143   ulg dopp, dopl;
144   ulg assosp, assosl;
145   ulg compp, compl;                                             /* Complex file info! */
146   ulg footpgp, footpgl;
147   ulg orignamep, orignamel;
148   ulg annoownp, annoownl;
149   ulg annobnp, annobnl;
150 } __attribute__((packed));
151
152 ulg txl, lbf;
153
154 int
155 cc(void)
156 {
157   if (txl)
158         {
159           txl--;
160           return gb();
161         }
162   return -1;
163 }
164
165 char lb[86];
166 int lbi;
167
168 void
169 flb(void)
170 {
171   lb[lbi++] = '\n';
172   lb[lbi] = 0;
173   fputs(lb, fo);
174   lbi = 0;
175 }
176
177 void
178 pc(int c)
179 {
180   if (lbi >= 80)
181         {
182           int lc = lbi;
183           while (lc > 0 && lb[--lc] != ' ')
184                 ;
185           if (!lc)
186                 {
187                   lbf++;
188                   flb();
189                 }
190           else
191                 {
192                   char exb[80];
193                   lb[lbi] = 0;
194                   lb[lc] = 0;
195                   lbi = lc++;
196                   strcpy(exb, lb+lc);
197                   flb();
198                   strcpy(lb, exb);
199                   lbi = strlen(lb);
200                 }
201         }
202   lb[lbi++] = c;
203 }
204
205 void
206 text(void)
207 {
208   int c;
209
210   for(;;)
211         switch (c = cc())
212           {
213           case -1:
214                 flb();
215                 if (lbf)
216                   printf("%d line breaks failed\n", lbf);
217                 return;
218           case 12:
219                 flb();
220                 fputc(12, fo);
221                 break;
222           case 13:
223                 lb[lbi++] = '\n';
224                 /* FALL-THRU */
225           case 11:
226                 flb();
227                 break;
228           case 9:
229           case 14:
230                 pc(9);
231                 break;
232           case 31:
233                 pc('-');
234                 break;
235           case 7:
236           case 19:
237           case 20:
238           case 21:
239                 break;
240           case 160:
241                 pc('~');
242                 break;
243           default:
244                 pc(c);
245           }
246 }
247
248 void
249 unword(void)
250 {
251   struct fib fib;
252   ulg where = ftell(fi);
253
254 printf("%d %d\n", (int)&(((struct fib *)0)->compp), where);
255   printf("Reading %d bytes of file header\n", sizeof(fib));
256   if (fread(&fib, sizeof(fib), 1, fi) != 1)
257         {
258           fprintf(stderr, "FIB read error!\n");
259           exit(1);
260         }
261   if (fib.magic != 0xa5db && fib.magic != 0xa5dc)
262         {
263           fprintf(stderr, "Black magic!\n");
264           exit(1);
265         }
266   printf("Lang=%d, charset=[%d,%d]\n", fib.lang, fib.textcharset, fib.tablecharset);
267   if (fib.flags1 & 4)
268          {
269            if (fib.magic == 0xa5db)
270              puts("Complex format, old magic");
271            else
272              {
273                     printf("Complex format, abs start=0x%x, len=%d\n", fib.compp + where, fib.compl);
274                fprintf(stderr, "Fast-saved format not supported yet!\n");
275                exit(1);
276                   }
277         }
278   if (fib.flags2 & 1)
279         {
280           fprintf(stderr, "Encrypted files not supported yet!\n");
281           exit(1);
282         }
283   if (fib.flags2 & 0x1000)
284         puts("Extended charsets detected");
285   printf("First text char at 0x%x, len=%d\n", fib.firsttextchar + where, fib.textlen);
286   fseek(fi, fib.firsttextchar + where, SEEK_SET);
287   txl = fib.textlen;
288   text();
289 }
290
291 void
292 convert(void)
293 {
294   word id = gw();
295   if (id == 0xa5db || id == 0xa5dc)
296         {
297           fseek(fi, 0, SEEK_SET);
298           puts("Plain M$-Word file...");
299         }
300   else if (id == 0xcfd0)
301         {
302           puts("OLE file...");
303           ole();
304         }
305   else
306         {
307           fprintf(stderr, "Unknown file format!\n");
308           exit(1);
309         }
310   unword();
311 }
312
313 int
314 main(int argc, char **argv)
315 {
316   if (argc != 3)
317         {
318           fprintf(stderr, "Usage: unword <from> <to>\n");
319           return 1;
320         }
321   if (!(fi = fopen(argv[1], "r")))
322         {
323           fprintf(stderr, "Unable to open input file: %m\n");
324           return 1;
325         }
326   if (!(fo = fopen(argv[2], "w")))
327         {
328           fprintf(stderr, "Unable to open output file: %m\n");
329           return 1;
330         }
331   convert();
332   return 0;
333 }