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