1 /*
2 * TEXTFILE.C
3 *
4 * Written on 30-Jul-90 by jim nutt. Changes on 10-Jul-94 by John Dennis.
5 * Changes by Frank Adams (dialog boxes etc.).
6 * Released to the public domain.
7 *
8 * Handles import and export of text files.
9 */
10
11 #include <stdio.h>
12 #include <stdlib.h>
13 #include <string.h>
14 #include <time.h>
15 #include <ctype.h>
16
17 #if defined(PACIFIC)
18 #include <unixio.h>
19 #elif defined(SASC)
20 #include <fcntl.h>
21 #elif defined(UNIX) || defined(__DJGPP__)
22 #include <unistd.h>
23 #else
24 #include <io.h>
25 #endif
26
27 #include <huskylib/compiler.h>
28
29 #if defined(UNIX) || defined(__EMX__) || defined(__DJGPP__)
30 #define HAVE_POPEN
31 #endif
32
33 #ifdef __MINGW32__
34 #define isatty _isatty
35 #endif
36
37 #include "addr.h"
38 #include "config.h"
39 #include "nedit.h"
40 #include "msged.h"
41 #include "winsys.h"
42 #include "menu.h"
43 #include "dialogs.h"
44 #include "memextra.h"
45 #include "strextra.h"
46 #include "wrap.h"
47 #include "date.h"
48 #include "nshow.h"
49 #include "quote.h"
50 #include "textfile.h"
51 #include "filedlg.h"
52 #include "keys.h"
53
54 #define TEXTLEN 2048
55
56 /* this routine expands tabs in imported files and filters out
57 other control characters */
58
filter_buffer(char * buf,int size)59 void filter_buffer(char *buf, int size)
60 {
61 char tempbuf[TEXTLEN];
62 char *s = tempbuf;
63 char *d = buf;
64 int y = 0, i=0;
65
66 if (size)
67 {
68 memmove(tempbuf, buf, size);
69 tempbuf[size-1] = '\0';
70
71 for (;*s && (size - 1);s++)
72 {
73 if (*s < ' ' && *s >= 0)
74 {
75 switch (*s)
76 {
77 case '\t':
78 i = (((y >> 3) + 1) << 3) - y;
79 while (size - 1 && i)
80 {
81 *d++ = ' ';
82 y++;
83 size--;
84 i--;
85 }
86 break;
87 case '\n':
88 *d++='\n';
89 y++;
90 size--;
91 break;
92 default:
93 *d++=' ';
94 y++;
95 size--;
96 break;
97 }
98 }
99 else
100 {
101 *d++ = *s;
102 y++;
103 size--;
104 }
105 }
106 }
107 *d = '\0';
108 }
109
import(LINE * l)110 void import(LINE * l)
111 {
112 char *fn;
113 static char *fname = NULL;
114 static char *line = NULL;
115 char *temp;
116 FILE *fp;
117 LINE *n;
118 int ret;
119
120 if (fname == NULL) fname = xmalloc(PATHLEN);
121 if (line == NULL) line = xmalloc(TEXTLEN);
122
123 fn = xmalloc(PATHLEN + 1);
124
125 if (ST->infile)
126 {
127 strcpy(fn, ST->infile);
128 }
129 else
130 {
131 fn[0] = '\0';
132 }
133
134 ret = FileDialog(fn, "Select a File to Import");
135
136 release(ST->infile);
137 ST->infile = xstrdup(fn);
138
139 TTCurSet(1);
140
141 if (!ret)
142 {
143 xfree(fn);
144 return;
145 }
146
147 fn = shell_expand(fn);
148
149 fp = fopen(fn, "r");
150 if (fp != NULL)
151 {
152 if (SW->importfn)
153 {
154 if (strstr(fn, "\\") || strstr(fn, "/"))
155 {
156 temp = getfilename(fn);
157 }
158 else
159 {
160 temp = strupr(fn);
161 }
162 strcpy(fname, temp);
163 sprintf(line, " ----- %s begins -----\n", fname);
164 l->text = strdup(line);
165 }
166
167 while (fgets(line, TEXTLEN, fp) != NULL)
168 {
169 filter_buffer(line, TEXTLEN);
170
171 if (l->text != NULL)
172 {
173 n = xcalloc(1, sizeof *n);
174 n->prev = l;
175 n->next = l->next;
176
177 if (n->next != NULL)
178 {
179 n->next->prev = n;
180 }
181
182 l->next = n;
183 l = n;
184 }
185 else
186 {
187 n = l;
188 }
189
190 /* softcrxlat functionality moved to readmail.c, because it
191 should take place in the transport charset, not in the
192 local charset! */
193
194 n->text = strdup(line);
195 if (strlen(n->text) > (size_t) SW->rm)
196 {
197 l = n->next;
198 wrap(n, 1, maxy, SW->rm);
199
200 if (!l)
201 {
202 while (n->next)
203 {
204 n = n->next;
205 }
206 }
207 else
208 {
209 n = l->prev;
210 }
211
212 l = n;
213 }
214 }
215
216 if (SW->importfn)
217 {
218 if (l->text != NULL)
219 {
220 n = xcalloc(1, sizeof *n);
221 n->prev = l;
222 n->next = l->next;
223
224 if (n->next != NULL)
225 {
226 n->next->prev = n;
227 }
228
229 l->next = n;
230 l = n;
231 }
232 sprintf(line, " ----- %s ends -----\n", fname);
233 l->text = strdup(line);
234 }
235 fclose(fp);
236 }
237 xfree(fn);
238 }
239
240 /*
241 * getfilename; Used to isolate the filename on an import so that
242 * it does not import directories into the message. Used only by
243 * import().
244 */
245
getfilename(char * buf)246 char *getfilename(char *buf)
247 {
248 int x, y;
249 char tempch[2];
250 static char filename[PATHLEN + 1] = "";
251
252 for (x = 0; x <= strlen(buf); x++)
253 {
254 if (buf[strlen(buf) - x] == '\\' || buf[strlen(buf) - x] == '/')
255 {
256 break;
257 }
258 }
259
260 for (y = strlen(buf) - x + 1; y <= strlen(buf); y++)
261 {
262 tempch[0] = buf[y];
263 tempch[1] = '\0';
264 if (y == strlen(buf) - x + 1)
265 {
266 strcpy(filename, tempch);
267 }
268 else
269 {
270 strcat(filename, tempch);
271 }
272 }
273
274 return filename;
275 }
276
277
export_text(msg * mesg,LINE * line)278 void export_text(msg *mesg, LINE *line)
279 {
280 LINE *l;
281 FILE *f = NULL;
282 int destination = 0, mode = 0, ret = 0, x1, x2;
283 char fn[2048];
284 int (*closefunc)(FILE *) = NULL;
285
286 static char *destinations[] = {
287 "Write to File",
288 "Print",
289 "Pipe into external Program",
290 "Cancel",
291 NULL
292 };
293 static char *modes[] = {
294 "As Plain Text",
295 /* "As Quoted Text", */
296 "Binary (for re-importing into Msged)",
297 "Cancel",
298 NULL
299 };
300
301 /* Find out if we want to print a message or only a piece of text */
302 if (mesg != NULL)
303 {
304 l = mesg->text;
305 }
306 else
307 {
308 l = line;
309 }
310
311 /* Select the export destination */
312
313 x1 = maxx/2 - 19; if (x1 < 0) x1 = 0;
314 x2 = x1 + 38;
315 destination = DoMenu(x1, 10, x2, 13, destinations, 0,
316 SELBOX_WRTMODE, "Export Destination");
317
318 switch (destination)
319 {
320 case 0: /* as file */
321 if (ST->outfile)
322 {
323 strcpy(fn, ST->outfile);
324 }
325 else
326 {
327 strcpy(fn, "");
328 }
329 if ((ret = FileDialog(fn, "Select File to Write to")) <= 0)
330 {
331 return;
332 }
333 xfree(ST->outfile);
334 ST->outfile = strdup(fn);
335 break;
336
337 case 1: /* print */
338 #ifdef UNIX
339 destination = 2;
340 if (ST->printer != NULL)
341 {
342 sprintf(fn, "lpr %s -", ST->printer);
343 }
344 else
345 {
346 sprintf(fn, "lpr -");
347 }
348 break;
349 #else
350 if (ST->printer != NULL)
351 {
352 strcpy(fn, ST->printer);
353 }
354 else
355 {
356 strcpy(fn, "PRN");
357 }
358 #endif
359 break;
360
361 case 2: /* pipe */
362 fn[0] = '\0';
363 #ifdef HAVE_POPEN
364 if (!GetString("Pipe Text To External Program", "Command Line", fn,
365 2047))
366 {
367 return;
368 }
369 #else
370 ChoiceBox("Not Implemented",
371 "This version of Msged does not support piping.",
372 "OK", NULL, NULL);
373 return;
374 #endif
375 break;
376
377 default: /* cancel */
378 return;
379 }
380
381
382 /* select the output mode, if a whole msg is exported */
383
384 if (mesg != NULL && destination != 1)
385 {
386 mode = DoMenu(x1, 10, x2, 13, modes, 0,
387 SELBOX_WRTMODE, "Export Mode");
388 if (mode < 0 || mode > 1)
389 {
390 return;
391 }
392 }
393
394
395 /* Try to open the output medium */
396
397 switch (destination)
398 {
399 case 0: /* file */
400 case 1:
401 closefunc = fclose;
402 if (((f = fopen(fn, "r")) != NULL) && (!isatty(fileno(f)))
403 #ifdef __DJGPP__ /* DJGPP's isatty is buggy, it returns 0 for "PRN" */
404 && destination != 1
405 #endif
406 )
407 {
408 ret = ChoiceBox("Attention", "File already exists!",
409 "Append", "Overwrite", "Cancel");
410
411 switch(ret)
412 {
413 case ID_ONE: /* append */
414 fclose(f);
415 f = fopen(fn, "a+");
416 if (f != NULL) fseek(f, 0, SEEK_END);
417 break;
418 case ID_TWO:
419 fclose(f);
420 f = fopen(fn, "w");
421 break;
422
423 case Key_Esc:
424 case ID_THREE:
425 fclose(f);
426 return;
427 default:
428 abort();
429 }
430 }
431 else
432 {
433 if (f != NULL)
434 fclose(f);
435 f = fopen(fn, "w");
436 }
437
438 if (f == NULL)
439 {
440 ChoiceBox("Error", "Cannot write to file!", "OK", NULL, NULL);
441 return;
442 }
443 break;
444
445 case 2:
446 #ifdef HAVE_POPEN
447 closefunc = pclose;
448 f = popen(fn, "w");
449 if (f == NULL)
450 {
451 ChoiceBox("Error", "Cannot execute program!", "OK", NULL, NULL);
452 return;
453 }
454 #endif
455 break;
456 }
457
458 /* TODO: Quote Mode */
459
460 /* write a header, if possible and desired */
461
462 if (mode == 0 && mesg != NULL)
463 {
464 fprintf(f, "Date: %s", itime(mesg->timestamp));
465 fprintf(f, "\nFrom: %s", mesg->isfrom ? mesg->isfrom : "");
466 fprintf(f, " of %s", show_address(&mesg->from));
467 fprintf(f, "\nTo: %s", mesg->isto ? mesg->isto : "");
468
469 if (CurArea.netmail)
470 {
471 fprintf(f, " of %s", show_address(&mesg->to));
472 }
473
474 fprintf(f, "\nSubj: %s", mesg->subj ? mesg->subj : "");
475
476 MakeMsgAttrs(fn, &mesg->attrib, mesg->scanned, mesg->times_read);
477
478 fprintf(f, "\nAttr: %s", fn);
479 fprintf(f, "\nConf: %-30s", CurArea.description);
480 fprintf(f, "\n\n");
481 }
482
483 /* write the message text */
484
485 while (l != NULL)
486 {
487 if (l->text && (*(l->text) != '\01' || SW->shownotes))
488 {
489 fputs(l->text, f);
490 if (!strchr(l->text, '\n') && (mode != 1))
491 {
492 fprintf(f, "\n");
493 }
494 }
495 l = l->next;
496 }
497
498 /* if output is to printer output a formfeed */
499 if (isatty(fileno(f))
500 #ifdef __DJGPP__ /* see above */
501 || destination == 1
502 #endif
503 )
504 {
505 fputc(12, f);
506 }
507
508 (*closefunc)(f);
509 }
510
511 /* called when exporting an anchor block from the editor */
export(LINE * f)512 void export(LINE * f)
513 {
514
515 export_text(NULL, f);
516 }
517
518 /* called when exporting while in reading mode */
writetxt(void)519 void writetxt(void)
520 {
521 export_text(message, NULL);
522 }
523