about summary refs log tree commit diff stats
path: root/src/LYPrint.c
blob: b9b6984919290d624092325fa461374581e65f68 (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999pre { line-height: 125%; }
td.linenos .normal { color: inherit; background-color: transparent; padding-left: 5px; padding-right: 5px; }
span.linenos { color: inherit; background-color: transparent; padding-left: 5px; padding-right: 5px; }
td.linenos .special { color: #000000; background-color: #ffffc0; padding-left: 5px; padding-right: 5px; }
span.linenos.special { color: #000000; background-color: #ffffc0; padding-left: 5px; padding-right: 5px; }
.highlight .hll { background-color: #ffffcc }
.highlight .c { color: #888888 } /* Comment */
.highlight .err { color: #a61717; background-color: #e3d2d2 } /* Error */
.highlight .k { color: #008800; font-weight: bold } /* Keyword */
.highlight .ch { color: #888888 } /* Comment.Hashbang */
.highlight .cm { color: #888888 } /* Comment.Multiline */
.highlight .cp { color: #cc0000; font-weight: bold } /* Comment.Preproc */
.highlight .cpf { color: #888888 } /* Comment.PreprocFile */
.highlight .c1 { color: #888888 } /* Comment.Single */
.highlight .cs { color: #cc0000; font-weight: bold; background-color: #fff0f0 } /* Comment.Special */
.highlight .gd { color: #000000; background-color: #ffdddd } /* Generic.Deleted */
.highlight .ge { font-style: italic } /* Generic.Emph */
.highlight .ges { font-weight: bold; font-style: italic } /* Generic.EmphStrong */
.highlight .gr { color: #aa0000 } /* Generic.Error */
.highlight .gh { color: #333333 } /* Generic.Heading */
.highlight .gi { color: #000000; background-color: #ddffdd } /* Generic.Inserted */
.highlight .go { color: #888888 } /* Generic.Output */
.highlight .gp { color: #555555 } /* Generic.Prompt */
.highlight .gs { font-weight: bold } /* Generic.Strong */
.highlight .gu { color: #666666 } /* Generic.Subheading */
.highlight .gt { color: #aa0000 } /* Generic.Traceback */
.highlight .kc { color: #008800; font-weight: bold } /* Keyword.Constant */
.highlight .kd { color: #008800; font-weight: bold } /* Keyword.Declaration */
.highlight .kn { color: #008800; font-weight: bold } /* Keyword.Namespace */
.highlight .kp { color: #008800 } /* Keyword.Pseudo */
.highlight .kr { color: #008800; font-weight: bold } /* Keyword.Reserved */
.highlight .kt { color: #888888; font-weight: bold } /* Keyword.Type */
.highlight .m { color: #0000DD; font-weight: bold } /* Literal.Number */
.highlight .s { color: #dd2200; background-color: #fff0f0 } /* Literal.String */
.highlight .na { color: #336699 } /* Name.Attribute */
.highlight .nb { color: #003388 } /* Name.Builtin */
.highlight .nc { color: #bb0066; font-weight: bold } /* Name.Class */
.highlight .no { color: #003366; font-weight: bold } /* Name.Constant */
.highlight .nd { color: #555555 } /* Name.Decorator */
.highlight .ne { color: #bb0066; font-weight: bold } /* Name.Exception */
.highlight .nf { color: #0066bb; font-weight: bold } /* Name.Function */
.highlight .nl { color: #336699; font-style: italic } /* Name.Label */
.highlight .nn { color: #bb0066; font-weight: bold } /* Name.Namespace */
.highlight .py { color: #336699; font-weight: bold } /* Name.Property */
.highlight .nt { color: #bb0066; font-weight: bold } /* Name.Tag */
.highlight .nv { color: #336699 } /* Name.Variable */
.highlight .ow { color: #008800 } /* Operator.Word */
.highlight .w { color: #bbbbbb } /* Text.Whitespace */
.highlight .mb { color: #0000DD; font-weight: bold } /* Literal.Number.Bin */
.highlight .mf { color: #0000DD; font-weight: bold } /* Literal.Number.Float */
.highlight .mh { color: #0000DD; font-weight: bold } /* Literal.Number.Hex */
.highlight .mi { color: #0000DD; font-weight: bold } /* Literal.Number.Integer */
.highlight .mo { color: #0000DD; font-weight: bold } /* Literal.Number.Oct */
.highlight .sa { color: #dd2200; background-color: #fff0f0 } /* Literal.String.Affix */
.highlight .sb { color: #dd2200; background-color: #fff0f0 } /* Literal.String.Backtick */
.highlight .sc { color: #dd2200; background-color: #fff0f0 } /* Literal.String.Char */
.highlight .dl { color: #dd2200; background-color: #fff0f0 } /* Literal.String.Delimiter */
.highlight .sd { color: #dd2200; background-color: #fff0f0 } /* Literal.String.Doc */
.highlight .s2 { color: #dd2200; background-color: #fff0f0 } /* Literal.String.Double */
.highlight .se { color: #0044dd; background-color: #fff0f0 } /* Literal.String.Escape */
.highlight .sh { color: #dd2200; background-color: #fff0f0 } /* Literal.String.Heredoc */
.highlight .si { color: #3333bb; background-color: #fff0f0 } /* Literal.String.Interpol */
.highlight .sx { color: #22bb22; background-color: #f0fff0 } /* Literal.String.Other */
.highlight .sr { color: #008800; background-color: #fff0ff } /* Literal.String.Regex */
.highlight .s1 { color: #dd2200; background-color: #fff0f0 } /* Literal.String.Single */
.highlight .ss { color: #aa6600; background-color: #fff0f0 } /* Literal.String.Symbol */
.highlight .bp { color: #003388 } /* Name.Builtin.Pseudo */
.highlight .fm { color: #0066bb; font-weight: bold } /* Name.Function.Magic */
.highlight .vc { color: #336699 } /* Name.Variable.Class */
.highlight .vg { color: #dd7700 } /* Name.Variable.Global */
.highlight .vi { color: #3333bb } /* Name.Variable.Instance */
.highlight .vm { color: #336699 } /* Name.Variable.Magic */
.highlight .il { color: #0000DD; font-weight: bold } /* Literal.Number.Integer.Long */
ranger v.1.5.4
==============
ranger is a console file manager with VI key bindings.  It provides a
minimalistic and nice curses interface with a view on the directory hierarchy.
The secondary task of ranger is to figure out which program you want to use to
open your files with.

This file describes ranger and how to get it to run.  For instructions on the
usage, please read the man page.  See doc/HACKING for development specific
information.  For configuration, check the files in ranger/defaults/.  They
are usually installed to /usr/lib/python*/site-packages/ranger/defaults/
and can be obtained with ranger's --copy-config option.

A note to packagers:  Versions meant for packaging are listed in the changelog
on the website.


About
-----
* Authors:     Check the copyright notices in each source file
* License:     GNU General Public License Version 3

* Website:     http://ranger.nongnu.org/
* Download:    http://ranger.nongnu.org/ranger-stable.tar.gz
* Bug reports: https://savannah.nongnu.org/bugs/?group=ranger&func=additem
* git clone    http://git.sv.gnu.org/r/ranger.git


Design Goals
------------
* An easily maintainable file manager in a high level language
* A quick way to switch directories and browse the file system
* Keep it small but useful, do one thing and do it well
* Console based, with smooth integration into the unix shell


Features
--------
* UTF-8 Support  (if your python copy supports it)
* Multi-column display
* Preview of the selected file/directory
* Common file operations (create/chmod/copy/delete/...)
* Renaming multiple files at once
* VIM-like console and hotkeys
* Automatically determine file types and run them with correct programs
* Change the directory of your shell after exiting ranger
* Tabs, Bookmarks, Mouse support


Dependencies
------------
* Python (tested with version 2.6, 2.7, 3.1, 3.2) with support for ncurses
  and (optionally) wide-unicode.
* A pager ("less" by default)

Optional:
* The "file" program for determining file types
* The python module "chardet", in case of encoding detection problems
* "sudo" to use the "run as root"-feature

Optional, for enhanced file previews (with "scope.sh"):
* img2txt (from caca-utils) for previewing images
* highlight for syntax highlighting of code
* atool for previews of archives
* lynx, w3m or elinks for previews of html pages
* pdftotext for pdf previews
* transmission-show for viewing bit-torrent information
* mediainfo for viewing information about media files


Installing
----------
Use the package manager of your operating system to install ranger.
Note that ranger can be started without installing by simply running ranger.py.

To install ranger manually:
    sudo make install

This translates roughly to:
    sudo python setup.py install --optimize=1 --record=install_log.txt

This also saves a list of all installed files to install_log.txt, which you can
use to uninstall ranger.


Getting Started
---------------
After starting ranger, you can use the Arrow Keys (or hjkl) to navigate, Enter
to open a file or type Q to quit.  The third column shows a preview of the
current file.  The second is the main column and the first shows the parent
directory.

Ranger can automatically copy default configuration files to ~/.config/ranger
if you run it with the switch --copy-config. (see ranger --help for a
description of that switch.)  Also check ranger/defaults/ for the default
configuration.
248'>1248 1249 1250 1251 1252 1253 1254 1255 1256 1257 1258 1259 1260 1261 1262 1263 1264 1265 1266 1267 1268 1269 1270 1271 1272 1273 1274 1275 1276 1277 1278 1279 1280 1281 1282 1283 1284 1285 1286 1287 1288 1289 1290 1291 1292 1293 1294 1295 1296 1297 1298 1299 1300 1301 1302 1303 1304 1305 1306 1307 1308 1309 1310 1311 1312 1313 1314 1315 1316 1317 1318 1319 1320 1321 1322 1323 1324 1325 1326 1327 1328 1329 1330 1331 1332 1333 1334 1335 1336 1337 1338 1339 1340 1341 1342 1343 1344 1345 1346 1347 1348 1349 1350 1351 1352 1353 1354 1355 1356 1357 1358 1359 1360 1361 1362 1363 1364 1365 1366 1367 1368 1369 1370 1371 1372 1373 1374 1375 1376 1377 1378 1379 1380 1381 1382 1383 1384 1385 1386 1387 1388 1389 1390 1391 1392 1393 1394 1395 1396 1397 1398 1399 1400 1401 1402 1403 1404 1405 1406 1407 1408 1409 1410 1411 1412 1413 1414 1415 1416 1417 1418 1419 1420 1421 1422 1423 1424 1425 1426 1427 1428 1429 1430 1431 1432 1433 1434 1435 1436 1437 1438 1439 1440 1441 1442 1443 1444 1445 1446 1447 1448 1449 1450 1451 1452 1453 1454 1455 1456 1457 1458 1459 1460 1461 1462 1463 1464 1465 1466 1467
#include <HTUtils.h>
#include <HTAccess.h>
#include <HTList.h>
#include <HTAlert.h>
#include <HTFile.h>
#include <LYCurses.h>
#include <GridText.h>
#include <LYUtils.h>
#include <LYPrint.h>
#include <LYGlobalDefs.h>
#include <LYSignal.h>
#include <LYStrings.h>
#include <LYClean.h>
#include <LYGetFile.h>
#include <LYHistory.h>
#include <LYList.h>
#include <LYCharSets.h>  /* To get current charset for mail header. */

#include <LYLeaks.h>

#define CancelPrint(msg) HTInfoMsg(msg); goto done
#define CannotPrint(msg) HTAlert(msg); goto done

/*
 *  printfile prints out the current file minus the links and targets
 *  to a variety of places
 */

/* it parses an incoming link that looks like
 *
 *  LYNXPRINT://LOCAL_FILE/lines=##
 *  LYNXPRINT://MAIL_FILE/lines=##
 *  LYNXPRINT://TO_SCREEN/lines=##
 *  LYNXPRINT://LPANSI/lines=##
 *  LYNXPRINT://PRINTER/lines=##/number=#
 */

#define TO_FILE   1
#define TO_SCREEN 2
/*
 * "lpansi.c"
 * Original author: Gary Day (gday@comp.uark.edu), 11/30/93
 * Current version: 2.1 by Noel Hunter (noel@wfu.edu), 10/20/94
 *
 * Basic structure based on print -- format files for printing from
 * _Practical_C_Programming by Steve Oualline, O'Reilly & Associates
 *
 * adapted from the README for lpansi.c v2.1, dated 10/20/1994:
 *		    Print to ANSI printer on local terminal
 *     The VT100 standard defines printer on and off escape sequences,
 *     esc[5i is printer on, and esc[4i is printer off.
 *
 * incorporate the idea of "lpansi" directly into LYPrint.c - HN
 */
#define LPANSI	  3
#define MAIL	  4
#define PRINTER   5

#if USE_VMS_MAILER
PRIVATE int remove_quotes PARAMS((char *string));
#endif /* USE_VMS_MAILER */

PRIVATE  char* subject_translate8bit PARAMS((char *source));

#define LYNX_PRINT_TITLE   0
#define LYNX_PRINT_URL     1
#define LYNX_PRINT_DATE    2
#define LYNX_PRINT_LASTMOD 3

#define MAX_PUTENV 4

PRIVATE void set_environ ARGS3(
	int,		name,
	CONST char *,	value,
	CONST char *,	no_value)
{
    static CONST char *names[MAX_PUTENV] = {
	"LYNX_PRINT_TITLE",
	"LYNX_PRINT_URL",
	"LYNX_PRINT_DATE",
	"LYNX_PRINT_LASTMOD",
    };
    static char *pointers[MAX_PUTENV];
    char *envbuffer = 0;
#ifdef VMS
#define SET_ENVIRON(name, value, no_value) set_environ(name, value, no_value)
    char temp[80];
    StrAllocCopy(envbuffer, value);
    if (!(envbuffer && *envbuffer))
	StrAllocCopy(envbuffer, no_value);
    Define_VMSLogical(strcpy(temp, names[name]), envbuffer);
    FREE(envbuffer);
#else
#define SET_ENVIRON(name, value, no_value) set_environ(name, value, "")
    /*
     * Once we've given a string to 'putenv()', we must not free it until we
     * give it a string to replace it.
     */
    StrAllocCopy(envbuffer, names[name]);
    StrAllocCat(envbuffer, "=");
    StrAllocCat(envbuffer, value ? value : no_value);
    putenv(envbuffer);
    FREE(pointers[name]);
    pointers[name] = envbuffer;
#endif
}

PRIVATE char *suggested_filename ARGS1(
	DocInfo *,	newdoc)
{
    char *cp, *sug_filename = 0;

    /*
     *	Load the suggested filename string. - FM
     */
    if (HText_getSugFname() != 0)
	StrAllocCopy(sug_filename, HText_getSugFname()); /* must be freed */
    else
	StrAllocCopy(sug_filename, newdoc->address); /* must be freed */
    /*
     *	Strip any gzip or compress suffix, if present. - FM
     */
    cp = NULL;
    if (strlen(sug_filename) > 3) {
	cp = (char *)&sug_filename[(strlen(sug_filename) - 3)];
	if ((*cp == '.' || *cp == '-' || *cp == '_') &&
	    !strcasecomp((cp + 1), "gz")) {
	    *cp = '\0';
	} else {
	    cp = NULL;
	}
    }
    if ((cp == NULL) && strlen(sug_filename) > 2) {
	cp = (char *)&sug_filename[(strlen(sug_filename) - 2)];
	if ((*cp == '.' || *cp == '-' || *cp == '_') &&
	    !strcasecomp((cp + 1), "Z")) {
	    *cp = '\0';
	}
    }
    return sug_filename;
}

PRIVATE void SetupFilename ARGS2(
	char *,		filename,
	char *,		sug_filename)
{
    HTFormat format;
    HTAtom *encoding;
    char *cp;

    LYstrncpy(filename, sug_filename, LY_MAXPATH-1);  /* add suggestion info */
    /* make the sug_filename conform to system specs */
    change_sug_filename(filename);
    if (!(HTisDocumentSource())
     && (cp = strrchr(filename, '.')) != NULL
     && (cp - filename) < LY_MAXPATH-5) {
	format = HTFileFormat(filename, &encoding, NULL);
	if (!strcasecomp(format->name, "text/html") ||
	    !IsUnityEnc(encoding)) {
	    strcpy(cp, ".txt");
	}
    }
}

#define FN_INIT 0
#define FN_READ 1
#define FN_DONE 2
#define FN_QUIT 3

#define PRINT_FLAG   0
#define GENERIC_FLAG 1

PRIVATE int RecallFilename ARGS5(
	char *,		filename,
	BOOLEAN *,	first,
	int *,		now,
	int *,		total,
	int,		flag)
{
    int ch;
    char *cp;
    int recall;

    /*
     * Set up the sug_filenames recall buffer.
     */
    if (*now < 0) {
	*total = (sug_filenames ? HTList_count(sug_filenames) : 0);
	*now = *total;
    }
    recall = ((*total >= 1) ? RECALL_URL : NORECALL);

    if ((ch = LYgetstr(filename, VISIBLE, LY_MAXPATH, recall)) < 0 ||
	*filename == '\0' || ch == UPARROW || ch == DNARROW) {
	if (recall && ch == UPARROW) {
	    if (*first) {
		*first = FALSE;
		/*
		 * Use the last Fname in the list.  - FM
		 */
		*now = 0;
	    } else {
		/*
		 * Go back to the previous Fname in the list.  - FM
		 */
		*now += 1;
	    }
	    if (*now >= *total) {
		/*
		 * Reset the *first flag, and use sug_file or a blank.  -
		 * FM
		 */
		*first = TRUE;
		*now = *total;
		_statusline(FILENAME_PROMPT);
		return FN_INIT;
	    } else if ((cp = (char *)HTList_objectAt(
					    sug_filenames,
					    *now)) != NULL) {
		LYstrncpy(filename, cp, LY_MAXPATH-1);
		if (*total == 1) {
		    _statusline(EDIT_THE_PREV_FILENAME);
		} else {
		    _statusline(EDIT_A_PREV_FILENAME);
		}
		return FN_READ;
	    }
	} else if (recall && ch == DNARROW) {
	    if (*first) {
		*first = FALSE;
		/*
		 * Use the first Fname in the list. - FM
		 */
		*now = *total - 1;
	    } else {
		/*
		 * Advance to the next Fname in the list. - FM
		 */
		*now -= 1;
	    }
	    if (*now < 0) {
		/*
		 * Set the *first flag, and use sug_file or a blank.  - FM
		 */
		*first = TRUE;
		*now = *total;
		_statusline(FILENAME_PROMPT);
		return FN_INIT;
	    } else if ((cp = (char *)HTList_objectAt(
					    sug_filenames,
					    *now)) != NULL) {
		LYstrncpy(filename, cp, LY_MAXPATH-1);
		if (*total == 1) {
		    _statusline(EDIT_THE_PREV_FILENAME);
		} else {
		    _statusline(EDIT_A_PREV_FILENAME);
		}
		return FN_READ;
	    }
	}

	/*
	 * Operation cancelled.
	 */
	if (flag == PRINT_FLAG)
	    HTInfoMsg(SAVE_REQUEST_CANCELLED);
	else if (flag == GENERIC_FLAG)
	    return FN_QUIT;

	return FN_QUIT;
    }
    return FN_DONE;
}

PRIVATE BOOLEAN confirm_by_pages ARGS3(
	char *,		prompt,
	int,		lines_in_file,
	int,		lines_per_page)
{
    int pages = lines_in_file/(lines_per_page+1);
    int c;

    /* count fractional pages ! */
    if ((lines_in_file % (LYlines+1)) > 0)
	pages++;

    if (pages > 4) {
	char *msg = 0;

	HTSprintf0(&msg, prompt, pages);
	c = HTConfirmDefault(msg, YES);
	FREE(msg);

	if (c == YES) {
	    LYaddstr("   Ok...");
	} else {
	    HTInfoMsg(PRINT_REQUEST_CANCELLED);
	    return FALSE;
	}
    }
    return TRUE;
}

PRIVATE void send_file_to_file ARGS3(
	DocInfo *,	newdoc,
	char *,		content_base,
	char *,		sug_filename)
{
    BOOLEAN FirstRecall = TRUE;
    BOOLEAN use_cte;
    CONST char *disp_charset;
    FILE *outfile_fp;
    char buffer[LY_MAXPATH];
    char filename[LY_MAXPATH];
    int FnameNum = -1;
    int FnameTotal;
    int c = 0;

    _statusline(FILENAME_PROMPT);
retry:
    SetupFilename(filename, sug_filename);
    if (lynx_save_space
     && (strlen(lynx_save_space) + strlen(filename)) < sizeof(filename)) {
	strcpy(buffer, lynx_save_space);
	strcat(buffer, filename);
	strcpy(filename, buffer);
    }
check_recall:
    switch (RecallFilename(filename,    &FirstRecall, &FnameNum,
			   &FnameTotal, PRINT_FLAG))  {
	case FN_INIT:
	    goto retry;
	case FN_READ:
	    goto check_recall;
	case FN_QUIT:
	    goto done;
	default:
	    break;
    }

    if (!LYValidateFilename(buffer, filename)) {
	CancelPrint(SAVE_REQUEST_CANCELLED);
    }

    /*
     * See if it already exists.
     */
    switch (LYValidateOutput(buffer)) {
    case 'Y':
	break;
    case 'N':
	_statusline(NEW_FILENAME_PROMPT);
	FirstRecall = TRUE;
	FnameNum = FnameTotal;
	goto retry;
    default:
	goto done;
    }

    /*
     * See if we can write to it.
     */
    CTRACE((tfp, "LYPrint: filename is %s, action is `%c'\n", buffer, c));

#ifdef HAVE_POPEN
    if (*buffer == '|') {
	if (no_shell) {
	    HTUserMsg(SPAWNING_DISABLED);
	    FirstRecall = TRUE;
	    FnameNum = FnameTotal;
	    goto retry;
	} else if ((outfile_fp = popen(buffer+1, "w")) == NULL) {
	    CTRACE((tfp, "LYPrint: errno is %d\n", errno));
	    HTAlert(CANNOT_WRITE_TO_FILE);
	    _statusline(NEW_FILENAME_PROMPT);
	    FirstRecall = TRUE;
	    FnameNum = FnameTotal;
	    goto retry;
	}
    } else
#endif
    if ((outfile_fp = (TOUPPER(c) == 'A'
	    ? LYAppendToTxtFile(buffer)
	    : LYNewTxtFile(buffer))) == NULL) {
	CTRACE((tfp, "LYPrint: errno is %d\n", errno));
	HTAlert(CANNOT_WRITE_TO_FILE);
	_statusline(NEW_FILENAME_PROMPT);
	FirstRecall = TRUE;
	FnameNum = FnameTotal;
	goto retry;
    }

    if (LYPrependBaseToSource && HTisDocumentSource()) {
	/*
	 * Added the document's base as a BASE tag to the top of the file.  May
	 * create technically invalid HTML, but will help get any partial or
	 * relative URLs resolved properly if no BASE tag is present to replace
	 * it.  - FM
	 *
	 * Add timestamp (last reload).
	 */

	fprintf(outfile_fp,
		"<!-- X-URL: %s -->\n", newdoc->address);
	if (HText_getDate() != NULL) {
	    fprintf(outfile_fp,
		"<!-- Date: %s -->\n", HText_getDate());
	    if (HText_getLastModified() != NULL
			&& strcmp(HText_getLastModified(), HText_getDate())
			&& strcmp(HText_getLastModified(), "Thu, 01 Jan 1970 00:00:01 GMT")) {
		fprintf(outfile_fp,
		    "<!-- Last-Modified: %s -->\n", HText_getLastModified());
	    }
	}

	fprintf(outfile_fp,
		"<BASE HREF=\"%s\">\n", content_base);
    }

    if (LYPrependCharsetToSource && HTisDocumentSource()) {
	/*
	 * Added the document's charset as a META CHARSET tag to the top of the
	 * file.  May create technically invalid HTML, but will help to resolve
	 * properly the document converted via chartrans:  printed document
	 * correspond to a display charset and we *should* override both
	 * assume_local_charset and original document's META CHARSET (if any).
	 *
	 * Currently, if several META CHARSETs are found Lynx uses the first
	 * only, and it is opposite to BASE where the original BASE in the
	 * <HEAD> overrides ones from the top.
	 *
	 * As in print-to-email we write charset only if the document has 8-bit
	 * characters, and we have no CJK or an unofficial "x-" charset.
	 */
	use_cte = HTLoadedDocumentEightbit();
	disp_charset = LYCharSet_UC[current_char_set].MIMEname;
	if (!use_cte || LYHaveCJKCharacterSet ||
	    strncasecomp(disp_charset, "x-", 2) == 0) {
	} else {
	    fprintf(outfile_fp,
		    "<META HTTP-EQUIV=\"Content-Type\" CONTENT=\"text/html; charset=%s\">\n\n",
		    disp_charset);
	}
    }

    print_wwwfile_to_fd(outfile_fp, FALSE);	/* FILE */
    if (keypad_mode)
	printlist(outfile_fp,FALSE);

#ifdef HAVE_POPEN
    if (LYIsPipeCommand(buffer))
	pclose(outfile_fp);
    else
#endif
	LYCloseOutput(outfile_fp);

#ifdef VMS
    if (0 == strncasecomp(buffer, "sys$disk:", 9)) {
	if (0 == strncmp((buffer+9), "[]", 2)) {
	    HTAddSugFilename(buffer+11);
	} else {
	    HTAddSugFilename(buffer+9);
	}
    } else {
	HTAddSugFilename(buffer);
    }
#else
    HTAddSugFilename(buffer);
#endif /* VMS */

done:
    return;
}

PRIVATE void send_file_to_mail ARGS3(
	DocInfo *,	newdoc,
	char *,		content_base,
	char *,		content_location)
{
    static BOOLEAN first_mail_preparsed = TRUE;

#if USE_VMS_MAILER
    BOOLEAN isPMDF = LYMailPMDF();
    FILE *hfd;
    char hdrfile[LY_MAXPATH];
#endif
    BOOL use_mime;
#if !CAN_PIPE_TO_MAILER
    char my_temp[LY_MAXPATH];
#endif

    BOOL use_cte;
    BOOL use_type;
    CONST char *disp_charset;
    FILE *outfile_fp;
    char *buffer = NULL;
    char *subject = NULL;
    char user_response[LINESIZE];

    if (!LYSystemMail())
	return;

    if (LYPreparsedSource && first_mail_preparsed &&
	HTisDocumentSource()) {
	if (HTConfirmDefault(CONFIRM_MAIL_SOURCE_PREPARSED, NO) == YES) {
	    LYaddstr("   Ok...");
	    first_mail_preparsed = FALSE;
	} else	{
	    CancelPrint(MAIL_REQUEST_CANCELLED);
	}
    }

    _statusline(MAIL_ADDRESS_PROMPT);
    LYstrncpy(user_response, personal_mail_address, sizeof(user_response)-1);
    if (LYgetstr(user_response, VISIBLE, sizeof(user_response), RECALL_MAIL) < 0 ||
	*user_response == '\0') {
	CancelPrint(MAIL_REQUEST_CANCELLED);
    }

    /*
     * Determine which mail headers should be sent.  Use Content-Type and
     * MIME-Version headers only if needed.  We need them if we are mailing
     * HTML source, or if we have 8-bit characters and will be sending
     * Content-Transfer-Encoding to indicate this.  We will append a charset
     * parameter to the Content-Type if we do not have an "x-" charset, and we
     * will include the Content-Transfer-Encoding only if we are appending the
     * charset parameter, because indicating an 8-bit transfer without also
     * indicating the charset can cause problems with many mailers.  - FM & KW
     */
    disp_charset = LYCharSet_UC[current_char_set].MIMEname;
    use_cte = HTLoadedDocumentEightbit();
    if (!(use_cte && strncasecomp(disp_charset, "x-", 2))) {
	disp_charset = NULL;
	use_cte = FALSE;
    }
    use_type = (BOOL) (disp_charset || HTisDocumentSource());

    /*
     * Use newdoc->title as a subject instead of sug_filename:  MORE readable
     * and 8-bit letters shouldn't be a problem - LP
     */
    /* change_sug_filename(sug_filename); */
    subject = subject_translate8bit(newdoc->title);

    if (newdoc->isHEAD) {
	/*
	 * Special case for mailing HEAD responce:  this is rather technical
	 * information, show URL.
	 */
	FREE(subject);
	StrAllocCopy(subject, "HEAD  ");
	StrAllocCat(subject, newdoc->address);
    }

#if USE_VMS_MAILER
    if (strchr(user_response,'@') && !strchr(user_response,':') &&
       !strchr(user_response,'%') && !strchr(user_response,'"')) {
	char *temp = 0;
	HTSprintf0(&temp, mail_adrs, user_response);
	LYstrncpy(user_response, temp, sizeof(user_response)-1);
	FREE(temp);
    }

    outfile_fp = LYOpenTemp(my_temp,
			    (HTisDocumentSource())
				    ? HTML_SUFFIX
				    : ".txt",
			    "w");
    if (outfile_fp == NULL) {
	CannotPrint(UNABLE_TO_OPEN_TEMPFILE);
    }

    if (isPMDF) {
	if ((hfd = LYOpenTemp(hdrfile, ".txt", "w")) == NULL) {
	    CannotPrint(UNABLE_TO_OPEN_TEMPFILE);
	}
	if (use_type) {
	    fprintf(hfd, "Mime-Version: 1.0\n");
	    if (use_cte) {
		fprintf(hfd, "Content-Transfer-Encoding: 8bit\n");
	    }
	}
	if (HTisDocumentSource()) {
	    /*
	     * Add Content-Type, Content-Location, and Content-Base headers for
	     * HTML source.  - FM
	     */
	    fprintf(hfd, "Content-Type: text/html");
	    if (disp_charset != NULL) {
		fprintf(hfd, "; charset=%s\n", disp_charset);
	    } else {
		fprintf(hfd, "\n");
	    }
	    fprintf(hfd, "Content-Base: %s\n", content_base);
	    fprintf(hfd, "Content-Location: %s\n", content_location);
	} else {
	    /*
	     * Add Content-Type:  text/plain if we have 8-bit characters and a
	     * valid charset for non-source documents.  - FM
	     */
	    if (disp_charset != NULL) {
		fprintf(hfd,
			"Content-Type: text/plain; charset=%s\n",
			disp_charset);
	    }
	}
	/*
	 *	X-URL header. - FM
	 */
	fprintf(hfd, "X-URL: %s\n", newdoc->address);
	/*
	 * For PMDF, put the subject in the header file and close it.  - FM
	 */
	fprintf(hfd, "Subject: %.70s\n\n", subject);
	LYCloseTempFP(hfd);
    }

    /*
     *  Write the contents to a temp file.
     */
    if (LYPrependBaseToSource && HTisDocumentSource()) {
	/*
	 * Added the document's base as a BASE tag to the top of the message
	 * body.  May create technically invalid HTML, but will help get any
	 * partial or relative URLs resolved properly if no BASE tag is present
	 * to replace it.  - FM
	 */
	fprintf(outfile_fp,
		"<!-- X-URL: %s -->\n<BASE HREF=\"%s\">\n\n",
		newdoc->address, content_base);
    } else if (!isPMDF) {
	fprintf(outfile_fp, "X-URL: %s\n\n", newdoc->address);
    }
    print_wwwfile_to_fd(outfile_fp, FALSE);	/* MAIL */
    if (keypad_mode)
	printlist(outfile_fp, FALSE);
    LYCloseTempFP(outfile_fp);

    buffer = NULL;
    if (isPMDF) {
	/*
	 * Now set up the command.  - FM
	 */
	HTSprintf0(&buffer,
		"%s %s %s,%s %s",
		system_mail,
		system_mail_flags,
		hdrfile,
		my_temp,
		user_response);
    } else {
	/*
	 * For "generic" VMS MAIL, include the subject in the command.  - FM
	 */
	remove_quotes(subject);
	HTSprintf0(&buffer,
		"%s %s/subject=\"%.70s\" %s %s",
		system_mail,
		system_mail_flags,
		subject,
		my_temp,
		user_response);
    }

    stop_curses();
    SetOutputMode( O_TEXT );
    printf(MAILING_FILE);
    LYSystem(buffer);
    LYSleepAlert();
    start_curses();
    SetOutputMode( O_BINARY );

    if (isPMDF)
	LYRemoveTemp(hdrfile);
    LYRemoveTemp(my_temp);
#else /* !VMS (Unix or DOS) */

#if CAN_PIPE_TO_MAILER
    outfile_fp = LYPipeToMailer();
#else
    outfile_fp = LYOpenTemp(my_temp, ".txt", "w");
#endif
    if (outfile_fp == NULL) {
	CannotPrint(MAIL_REQUEST_FAILED);
    }

    /*
     * Determine which mail headers should be sent.  Use Content-Type and
     * MIME-Version headers only if needed.  We need them if we are mailing
     * HTML source, or if we have 8-bit characters and will be sending
     * Content-Transfer-Encoding to indicate this.
     *
     * Send Content-Transfer-Encoding only if the document has 8-bit
     * characters.  Send a charset parameter only if the document has 8-bit
     * characters and we seem to have a valid charset.  - kw
     */
    use_cte = HTLoadedDocumentEightbit();
    disp_charset = LYCharSet_UC[current_char_set].MIMEname;
    /*
     * Don't send a charset if we have a CJK character set selected, since it
     * may not be appropriate for mail...  Also don't use an unofficial "x-"
     * charset.  - kw
     */
    if (!use_cte || LYHaveCJKCharacterSet ||
	strncasecomp(disp_charset, "x-", 2) == 0) {
	disp_charset = NULL;
    }
#ifdef NOTDEFINED
    /*  Enable this if indicating an 8-bit transfer without
     *  also indicating the charset causes problems. - kw */
    if (use_cte && !disp_charset)
	use_cte = FALSE;
#endif /* NOTDEFINED */
    use_type = (BOOL) (disp_charset || HTisDocumentSource());
    use_mime = (BOOL) (use_cte || use_type);

    if (use_mime) {
	fprintf(outfile_fp, "Mime-Version: 1.0\n");
	if (use_cte) {
	    fprintf(outfile_fp, "Content-Transfer-Encoding: 8bit\n");
	}
    }

    if (HTisDocumentSource()) {
	/*
	 * Add Content-Type, Content-Location, and Content-Base headers for
	 * HTML source.  - FM
	 */
	fprintf(outfile_fp, "Content-Type: text/html");
	if (disp_charset != NULL) {
	    fprintf(outfile_fp, "; charset=%s\n", disp_charset);
	} else {
	    fprintf(outfile_fp, "\n");
	}
    } else {
	/*
	 * Add Content-Type:  text/plain if we have 8-bit characters and a
	 * valid charset for non-source documents.  - KW
	 */
	if (disp_charset != NULL) {
	    fprintf(outfile_fp,
		    "Content-Type: text/plain; charset=%s\n",
		    disp_charset);
	}
    }
    /*
     * If we are using MIME headers, add content-base and content-location if
     * we have them.  This will always be the case if the document is source.
     * - kw
     */
    if (use_mime) {
	if (content_base)
	    fprintf(outfile_fp, "Content-Base: %s\n", content_base);
	if (content_location)
	    fprintf(outfile_fp, "Content-Location: %s\n", content_location);
    }

    /*
     *  Add the To, Subject, and X-URL headers. - FM
     */
    fprintf(outfile_fp, "To: %s\nSubject: %s\n", user_response, subject);
    fprintf(outfile_fp, "X-URL: %s\n\n", newdoc->address);

    if (LYPrependBaseToSource && HTisDocumentSource()) {
	/*
	 * Added the document's base as a BASE tag to the top of the message
	 * body.  May create technically invalid HTML, but will help get any
	 * partial or relative URLs resolved properly if no BASE tag is present
	 * to replace it.  - FM
	 */
	fprintf(outfile_fp,
		"<!-- X-URL: %s -->\n<BASE HREF=\"%s\">\n\n",
		newdoc->address, content_base);
    }
    print_wwwfile_to_fd(outfile_fp, FALSE);	/* MAIL */
    if (keypad_mode)
	printlist(outfile_fp, FALSE);

#if CAN_PIPE_TO_MAILER
    pclose(outfile_fp);
#else
    LYCloseOutput(outfile_fp);
    LYSendMailFile (
	    user_response,
	    my_temp,
	    subject,
	    "",
	    "");
    LYRemoveTemp(my_temp); /* Delete the tmpfile. */
#endif /* CAN_PIPE_TO_MAILER */
#endif /* USE_VMS_MAILER */

done:	/* send_file_to_mail() */
    FREE(buffer);
    FREE(subject);
    return;
}

PRIVATE void send_file_to_printer ARGS4(
	DocInfo *,	newdoc,
	char *,		content_base,
	char *,		sug_filename,
	int,		printer_number)
{
    BOOLEAN FirstRecall = TRUE;
    FILE *outfile_fp;
    char *the_command = 0;
    char my_file[LY_MAXPATH];
    char my_temp[LY_MAXPATH];
    int FnameTotal, FnameNum = -1;
    lynx_list_item_type *cur_printer;

    outfile_fp = LYOpenTemp(my_temp,
			    (HTisDocumentSource())
				    ? HTML_SUFFIX
				    : ".txt",
			    "w");
    if (outfile_fp == NULL) {
	CannotPrint(FILE_ALLOC_FAILED);
    }

    if (LYPrependBaseToSource && HTisDocumentSource()) {
	/*
	 * Added the document's base as a BASE tag to the top of the file.  May
	 * create technically invalid HTML, but will help get any partial or
	 * relative URLs resolved properly if no BASE tag is present to replace
	 * it.  - FM
	 */
	fprintf(outfile_fp,
		"<!-- X-URL: %s -->\n<BASE HREF=\"%s\">\n\n",
		newdoc->address, content_base);
    }
    print_wwwfile_to_fd(outfile_fp, FALSE);	/* PRINTER */
    if (keypad_mode)
	printlist(outfile_fp, FALSE);

    LYCloseTempFP(outfile_fp);

    /* find the right printer number */
    {
	int count=0;
	for (cur_printer = printers;
	     count < printer_number;
	     count++, cur_printer = cur_printer->next)
	    ; /* null body */
    }

    /*
     * Commands have the form "command %s [%s] [etc]" where %s is the filename
     * and the second optional %s is the suggested filename.
     */
    if (cur_printer->command == NULL) {
	CannotPrint(PRINTER_MISCONF_ERROR);
    }

    /*
     * Check for two '%s' and ask for the second filename argument if there
     * is.
     */
    if (HTCountCommandArgs (cur_printer->command) >= 2) {
	_statusline(FILENAME_PROMPT);
again:
	SetupFilename(my_file, sug_filename);
check_again:
	switch (RecallFilename(my_file,     &FirstRecall, &FnameNum,
			       &FnameTotal, PRINT_FLAG))  {
	    case FN_INIT:
		goto again;
	    case FN_READ:
		goto check_again;
	    case FN_QUIT:
		goto done;
	    default:
		break;
	}

	if (no_dotfiles || !show_dotfiles) {
	    if (*LYPathLeaf(my_file) == '.') {
		HTAlert(FILENAME_CANNOT_BE_DOT);
		_statusline(NEW_FILENAME_PROMPT);
		FirstRecall = TRUE;
		FnameNum = FnameTotal;
		goto again;
	    }
	}
	/*
	 * Cancel if the user entered "/dev/null" on Unix, or an "nl:" path
	 * on VMS.  - FM
	 */
	if (LYIsNullDevice(my_file)) {
	    CancelPrint(PRINT_REQUEST_CANCELLED);
	}
	HTAddSugFilename(my_file);
    }
#ifdef SH_EX	/* 1999/01/04 (Mon) 09:37:03 */
    else {
	my_file[0] = '\0';
    }

    HTAddParam (&the_command, cur_printer->command, 1, my_temp);
    if (my_file[0]) {
	HTAddParam (&the_command, cur_printer->command, 2, my_file);
	HTEndParam (&the_command, cur_printer->command, 3);
    } else {
	HTEndParam (&the_command, cur_printer->command, 2);
    }
#else
    HTAddParam (&the_command, cur_printer->command, 1, my_temp);
    HTAddParam (&the_command, cur_printer->command, 2, my_file);
    HTEndParam (&the_command, cur_printer->command, 2);
#endif

    /*
     * Move the cursor to the top of the screen so that output from system'd
     * commands don't scroll up the screen.
     */
    LYmove(1,1);

    stop_curses();
    CTRACE((tfp, "command: %s\n", the_command));
    SetOutputMode( O_TEXT );
    printf(PRINTING_FILE);
    /*
     * Set various bits of document information as environment variables, for
     * use by external print scripts/etc.  On UNIX, We assume there are values,
     * and leave NULL value checking up to the external PRINTER:  cmd/script -
     * KED
     */
    SET_ENVIRON(LYNX_PRINT_TITLE,   HText_getTitle(),        "No Title");
    SET_ENVIRON(LYNX_PRINT_URL,     newdoc->address,         "No URL");
    SET_ENVIRON(LYNX_PRINT_DATE,    HText_getDate(),         "No Date");
    SET_ENVIRON(LYNX_PRINT_LASTMOD, HText_getLastModified(), "No LastMod");

    LYSystem(the_command);
    FREE(the_command);
    LYRemoveTemp(my_temp);

    /*
     * Remove the various LYNX_PRINT_xxxx logicals.  - KED
     * [could use unsetenv(), but it's not portable]
     */
    SET_ENVIRON(LYNX_PRINT_TITLE,   "", "");
    SET_ENVIRON(LYNX_PRINT_URL,     "", "");
    SET_ENVIRON(LYNX_PRINT_DATE,    "", "");
    SET_ENVIRON(LYNX_PRINT_LASTMOD, "", "");

    fflush(stdout);
#ifndef VMS
    signal(SIGINT, cleanup_sig);
#endif /* !VMS */
#ifdef SH_EX
    fprintf(stdout, gettext(" Print job complete.\n"));
    fflush(stdout);
#endif
    SetOutputMode( O_BINARY );
    LYSleepMsg();
    start_curses();

done:	/* send_file_to_printer() */
    return;
}

PRIVATE void send_file_to_screen ARGS3(
	DocInfo *,	newdoc,
	char *,		content_base,
	BOOLEAN,	Lpansi)
{
    FILE *outfile_fp;
    char prompt[80];

    if (Lpansi) {
	_statusline(CHECK_PRINTER);
    } else {
	_statusline(PRESS_RETURN_TO_BEGIN);
    }

    *prompt = '\0';
    if (LYgetstr(prompt, VISIBLE, sizeof(prompt), NORECALL) < 0) {
	CancelPrint(PRINT_REQUEST_CANCELLED);
    }

    outfile_fp = stdout;

    stop_curses();
    SetOutputMode( O_TEXT );

#ifndef VMS
    signal(SIGINT, SIG_IGN);
#endif /* !VMS */

    if (LYPrependBaseToSource && HTisDocumentSource()) {
	/*
	 * Added the document's base as a BASE tag to the top of the file.  May
	 * create technically invalid HTML, but will help get any partial or
	 * relative URLs resolved properly if no BASE tag is present to replace
	 * it.  - FM
	 */
	fprintf(outfile_fp,
		"<!-- X-URL: %s -->\n<BASE HREF=\"%s\">\n\n",
		newdoc->address, content_base);
    }
    if (Lpansi)
	printf("\033[5i");
    print_wwwfile_to_fd(outfile_fp, FALSE);	/* SCREEN */
    if (keypad_mode)
	printlist(outfile_fp, FALSE);

#ifdef VMS
    if (HadVMSInterrupt) {
	HadVMSInterrupt = FALSE;
	start_curses();
	CancelPrint(PRINT_REQUEST_CANCELLED);
    }
#endif /* VMS */
    if (Lpansi) {
	printf("\n\014");	/* Form feed */
	printf("\033[4i");
	fflush(stdout);  /* refresh to screen */
	Lpansi = FALSE;
    } else {
	fprintf(stdout,"\n\n%s", PRESS_RETURN_TO_FINISH);
	fflush(stdout);		/* refresh to screen */
	(void) LYgetch();	/* grab some user input to pause */
#ifdef VMS
	HadVMSInterrupt = FALSE;
#endif /* VMS */
    }
#ifdef SH_EX
    fprintf(stdout,"\n");
#endif
    SetOutputMode( O_BINARY );
    start_curses();

done:	/* send_file_to_screen() */
    return;
}

PUBLIC int printfile ARGS1(
	DocInfo *,	newdoc)
{
    BOOLEAN Lpansi = FALSE;
    DocAddress WWWDoc;
    char *content_base = NULL;
    char *content_location = NULL;
    char *cp = NULL;
    char *link_info = NULL;
    char *sug_filename = NULL;
    int lines_in_file = 0;
    int pagelen = 0;
    int printer_number = 0;
    int type = 0;

    /*
     *	Extract useful info from URL.
     */
    StrAllocCopy(link_info, newdoc->address+12);

    /*
     *	Reload the file we want to print into memory.
     */
    LYpop(newdoc);
    WWWDoc.address = newdoc->address;
    WWWDoc.post_data = newdoc->post_data;
    WWWDoc.post_content_type = newdoc->post_content_type;
    WWWDoc.bookmark = newdoc->bookmark;
    WWWDoc.isHEAD = newdoc->isHEAD;
    WWWDoc.safe = newdoc->safe;
    if (!HTLoadAbsolute(&WWWDoc))
	return(NOT_FOUND);

    /*
     *	If we have an explicit content-base, we may use it even
     *	if not in source mode. - kw
     */
    if (HText_getContentBase()) {
	StrAllocCopy(content_base, HText_getContentBase());
	LYRemoveBlanks(content_base);
	if (!(content_base && *content_base)) {
	    FREE(content_base);
	}
    }
    /*
     *	If document is source, load the content_base
     *	and content_location strings. - FM
     */
    if (HTisDocumentSource()) {
	if (HText_getContentLocation()) {
	    StrAllocCopy(content_location, HText_getContentLocation());
	    LYRemoveBlanks(content_location);
	    if (!(content_location && *content_location)) {
		FREE(content_location);
	    }
	}
	if (!content_base) {
	    if ((content_location) && is_url(content_location)) {
		StrAllocCopy(content_base, content_location);
	    } else {
		StrAllocCopy(content_base, newdoc->address);
	    }
	}
	if (!content_location) {
	    StrAllocCopy(content_location, newdoc->address);
	}
    }

    sug_filename = suggested_filename(newdoc);

    /*
     *	Get the number of lines in the file.
     */
    if ((cp = strstr(link_info, "lines=")) != NULL) {
	/*
	 *  Terminate prev string here.
	 */
	*cp = '\0';
	/*
	 *  Number of characters in "lines=".
	 */
	cp += 6;

	lines_in_file = atoi(cp);
    }

    /*
     *	Determine the type.
     */
    if (strstr(link_info, "LOCAL_FILE")) {
	type = TO_FILE;
    } else if (strstr(link_info, "TO_SCREEN")) {
	type = TO_SCREEN;
    } else if (strstr(link_info, "LPANSI")) {
	Lpansi = TRUE;
	type = TO_SCREEN;
    } else if (strstr(link_info, "MAIL_FILE")) {
	type = MAIL;
    } else if (strstr(link_info, "PRINTER")) {
	type = PRINTER;

	if ((cp = strstr(link_info, "number=")) != NULL) {
	    /* number of characters in "number=" */
	    cp += 7;
	    printer_number = atoi(cp);
	}
	if ((cp = strstr(link_info, "pagelen=")) != NULL) {
	    /* number of characters in "pagelen=" */
	    cp += 8;
	    pagelen = atoi(cp);
	} else {
	    /* default to 66 lines */
	    pagelen = 66;
	}
    }

    /*
     *	Act on the request. - FM
     */
    switch (type) {

	case TO_FILE:
	    send_file_to_file(newdoc, content_base, sug_filename);
	    break;

	case MAIL:
	    send_file_to_mail(newdoc, content_base, content_location);
	    break;

	case TO_SCREEN:
	    if (confirm_by_pages(CONFIRM_LONG_SCREEN_PRINT, lines_in_file, LYlines))
		send_file_to_screen(newdoc, content_base, Lpansi);
	    break;

	case PRINTER:
	    if (confirm_by_pages(CONFIRM_LONG_PAGE_PRINT, lines_in_file, pagelen))
		send_file_to_printer(newdoc, content_base, sug_filename, printer_number);
	    break;

    } /* end switch */

    FREE(link_info);
    FREE(sug_filename);
    FREE(content_base);
    FREE(content_location);
    return(NORMAL);
}

#if USE_VMS_MAILER
PRIVATE int remove_quotes ARGS1(
	char *,		string)
{
   int i;

   for(i = 0; string[i] != '\0'; i++)
	if(string[i] == '"')
	   string[i] = ' ';
	else if(string[i] == '&')
	   string[i] = ' ';
	else if(string[i] == '|')
	   string[i] = ' ';

   return(0);
}
#endif /* USE_VMS_MAILER */

/*
 *  Mail subject may have 8-bit characters and they are in display charset.
 *  There is no stable practice for 8-bit subject encodings:
 *  MIME defines "quoted-printable" which holds charset info
 *  but most mailers still don't support it.  On the other hand
 *  many mailers send open 8-bit subjects without charset info
 *  and use local assumption for certain countries.  Besides that,
 *  obsolete SMTP software is not 8bit clean but still in use,
 *  it strips the characters in 128-160 range from subjects
 *  which may be a fault outside iso-8859-XX.
 *
 *  We translate subject to "outgoing_mail_charset" (defined in lynx.cfg)
 *  it may correspond to US-ASCII as the safest value or any other
 *  lynx character handler, -1 for no translation (so display charset).
 *
 *  Always returns a new allocated string which has to be freed.
 */
#include <LYCharUtils.h>
PRIVATE char* subject_translate8bit ARGS1(char *, source)
{
    char *target = NULL;

    int charset_in, charset_out;

    int i = outgoing_mail_charset;  /* from lynx.cfg, -1 by default */

    StrAllocCopy(target, source);
    if (i < 0
     || i == current_char_set
     || LYCharSet_UC[current_char_set].enc == UCT_ENC_CJK
     || LYCharSet_UC[i].enc == UCT_ENC_CJK) {
	return(target); /* OK */
    } else {
	charset_out = i;
	charset_in  = current_char_set;
    }

    LYUCTranslateBackHeaderText(&target, charset_in, charset_out, YES);

    return(target);
}

/*
 * print_options writes out the current printer choices to a file
 * so that the user can select printers in the same way that
 * they select all other links
 * printer links look like
 *  LYNXPRINT://LOCAL_FILE/lines=#	     print to a local file
 *  LYNXPRINT://TO_SCREEN/lines=#	     print to the screen
 *  LYNXPRINT://LPANSI/lines=#		     print to the local terminal
 *  LYNXPRINT://MAIL_FILE/lines=#	     mail the file
 *  LYNXPRINT://PRINTER/lines=#/number=#   print to printer number #
 */
PUBLIC int print_options ARGS3(
	char **,	newfile,
	CONST char *,	printed_url,
	int,		lines_in_file)
{
    static char my_temp[LY_MAXPATH] = "\0";
    char *buffer = 0;
    int count;
    int pages;
    FILE *fp0;
    lynx_list_item_type *cur_printer;

    if ((fp0 = InternalPageFP(my_temp, TRUE)) == 0)
	return(-1);

    LYLocalFileToURL(newfile, my_temp);

    BeginInternalPage(fp0, PRINT_OPTIONS_TITLE, PRINT_OPTIONS_HELP);

    fprintf(fp0, "<pre>\n");

    /*  pages = lines_in_file/66 + 1; */
    pages = (lines_in_file+65)/66;
    HTSprintf0(&buffer, "   <em>%s</em> %s\n   <em>%s</em> %d\n   <em>%s</em> %d %s %s\n",
	    gettext("Document:"), printed_url,
	    gettext("Number of lines:"), lines_in_file,
	    gettext("Number of pages:"), pages,
	    (pages > 1 ? gettext("pages") : gettext("page")),
	    gettext("(approximately)"));
    fputs(buffer, fp0);
    FREE(buffer);

    if (no_print || no_disk_save || child_lynx || no_mail)
	fprintf(fp0, "   <em>%s</em>\n", gettext("Some print functions have been disabled!"));

    fprintf(fp0, "\n%s\n",
	    (user_mode == NOVICE_MODE)
	    ? gettext("Standard print options:")
	    : gettext("Print options:"));

    if (child_lynx == FALSE && no_disk_save == FALSE && no_print == FALSE) {
	fprintf(fp0,
		"   <a href=\"%s//LOCAL_FILE/lines=%d\">%s</a>\n",
		STR_LYNXPRINT,
		lines_in_file,
		gettext("Save to a local file"));
    } else {
	fprintf(fp0,"   <em>%s</em>\n", gettext("Save to disk disabled"));
    }
    if (child_lynx == FALSE && no_mail == FALSE && local_host_only == FALSE)
	fprintf(fp0,
		"   <a href=\"%s//MAIL_FILE/lines=%d\">%s</a>\n",
		STR_LYNXPRINT,
		lines_in_file,
		gettext("Mail the file"));

#if defined(UNIX) || defined(VMS)
    fprintf(fp0,
	    "   <a href=\"%s//TO_SCREEN/lines=%d\">%s</a>\n",
	    STR_LYNXPRINT,
	    lines_in_file,
	    gettext("Print to the screen"));
    fprintf(fp0,
	    "   <a href=\"%s//LPANSI/lines=%d\">%s</a>\n",
	    STR_LYNXPRINT,
	    lines_in_file,
	    gettext("Print out on a printer attached to your vt100 terminal"));
#endif

    if (user_mode == NOVICE_MODE)
	fprintf(fp0, "\n%s\n", gettext("Local additions:"));

    for (count = 0, cur_printer = printers; cur_printer != NULL;
	cur_printer = cur_printer->next, count++)
    if (no_print == FALSE || cur_printer->always_enabled) {
	fprintf(fp0,
		"   <a href=\"%s//PRINTER/number=%d/pagelen=%d/lines=%d\">",
		STR_LYNXPRINT,
		count, cur_printer->pagelen, lines_in_file);
	fprintf(fp0, (cur_printer->name ?
		      cur_printer->name : "No Name Given"));
	fprintf(fp0, "</a>\n");
    }
    fprintf(fp0, "</pre>\n");
    EndInternalPage(fp0);
    LYCloseTempFP(fp0);

    LYforce_no_cache = TRUE;
    return(0);
}


/*
 *  General purpose filename getter.
 *
 *  Returns a pointer to an absolute filename string, if the input
 *  filename exists, and is readable.  Returns NULL if the input
 *  was cancelled (^G, or CR on empty input).
 *
 *  The pointer to the filename string needs to be free()'d by the
 *  caller (when non-NULL).
 *
 *  --KED  02/21/99
 */
PUBLIC char * GetFileName NOARGS
{
    struct stat stat_info;

    char  fbuf[LY_MAXPATH];
    char  tbuf[LY_MAXPATH];
    char *fn;

    BOOLEAN FirstRecall = TRUE;
    int     FnameNum    = -1;
    int     FnameTotal;


    _statusline(FILENAME_PROMPT);

retry:
    /*
     *  No initial filename.
     */
    SetupFilename (fbuf, "");

check_recall:
    /*
     *  Go get a filename (it would be nice to do TAB == filename-completion
     *  as the name is entered, but we'll save doing that for another time.
     */
    switch (RecallFilename (fbuf,        &FirstRecall,  &FnameNum,
			    &FnameTotal, GENERIC_FLAG)) {
	case FN_INIT:
	    goto retry;
	case FN_READ:
	    goto check_recall;
	case FN_QUIT:
	    goto quit;
	default:
	    break;
    }

    /*
     *  Add raw input form to list ... we may want to reuse/edit it on a
     *  subsequent call, etc.
     */
#ifdef VMS
    if (0 == strncasecomp (fbuf, "sys$disk:", 9)) {
	if (0 == strncmp ((fbuf+9), "[]", 2)) {
	    HTAddSugFilename (fbuf+11);
	} else {
	    HTAddSugFilename (fbuf+9);
	}
    } else {
	HTAddSugFilename (fbuf);
    }
#else
    HTAddSugFilename (fbuf);
#endif /* VMS */

    /*
     *  Expand tilde's, make filename absolute, etc.
     */
    if (!LYValidateFilename (tbuf, fbuf))
	goto quit;

    /*
     *  Check for file existence; readability.
     */
    if ((stat (tbuf, &stat_info) < 0) ||
       (!(S_ISREG(stat_info.st_mode)
#ifdef S_IFLNK
	 || S_ISLNK(stat_info.st_mode)
#endif /* S_IFLNK */
	))) {
	HTInfoMsg (FILE_DOES_NOT_EXIST);
	_statusline(FILE_DOES_NOT_EXIST_RE);
	FirstRecall = TRUE;
	FnameNum    = FnameTotal;
	goto retry;
    }

    if (!LYCanReadFile(tbuf)) {
	HTInfoMsg (FILE_NOT_READABLE);
	_statusline(FILE_NOT_READABLE_RE);
	FirstRecall = TRUE;
	FnameNum    = FnameTotal;
	goto retry;
    }

    /*
     *  We have a valid filename, and readable file.  Return it to the
     *  caller.
     *
     *  The returned pointer should be free()'d by the caller.
     *
     *  [For some silly reason, if we use StrAllocCopy() here, we get an
     *   "invalid pointer" reported in the Lynx.leaks file (if compiled
     *   with  --enable-find-leaks  turned on.  Dumb.]
     */
    if ((fn = typecallocn(char, strlen (tbuf) + 1)) == NULL)
	outofmem(__FILE__, "GetFileName");
    return (strcpy (fn, tbuf));


quit:
    /*
     *  The user cancelled the input (^G, or CR on empty input field).
     */
    return (NULL);
}