1 /*
2 This file is part of MOST.
3
4 Copyright (c) 1991, 1999, 2002, 2005-2018, 2019 John E. Davis
5
6 This program is free software; you can redistribute it and/or modify it
7 under the terms of the GNU General Public License as published by the Free
8 Software Foundation; either version 2 of the License, or (at your option)
9 any later version.
10
11 This program is distributed in the hope that it will be useful, but WITHOUT
12 ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
13 FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
14 more details.
15
16 You should have received a copy of the GNU General Public License along
17 with this program; if not, write to the Free Software Foundation, Inc., 675
18 Mass Ave, Cambridge, MA 02139, USA.
19 */
20 #include "config.h"
21
22 #include <stdio.h>
23 #include <ctype.h>
24
25 #ifdef HAVE_STDLIB_H
26 # include <stdlib.h>
27 #endif
28
29 #ifdef HAVE_UNISTD_H
30 # include <unistd.h>
31 #endif
32
33 #ifdef VMS
34 # include <rmsdef.h>
35 #endif
36
37 #include <errno.h>
38 #include <stdarg.h>
39
40 #include <slang.h>
41 #include "version.h"
42
43 #include "jdmacros.h"
44
45 #include "most.h"
46 #include "search.h"
47 #include "window.h"
48 #include "file.h"
49 #include "sysdep.h"
50 #include "keym.h"
51 #include "display.h"
52 #include "line.h"
53
54 int Most_S_Opt = 0; /* squeeze liness */
55 int Most_A_Opt = 1; /* automatically choose -b if necessary */
56 int Most_V_Opt = 0; /* display control chars */
57 int Most_B_Opt = 0; /* display Binary File */
58 int Most_T_Opt = 0; /* display tab as ^I-- valid only with V option */
59 int Most_D_Opt = 0; /* delete file mode (see ':D') */
60 int Most_K_Opt = 0; /* Display 8 bit unformatted (Kanji) */
61 int Most_Z_Opt = 0; /* Gunzip on the fly */
62 int Most_Want_Exit;
63 int Most_Secure_Mode;
64 int Most_Captive_Mode;
65 #if MOST_HAS_MMAP
66 int Most_Disable_MMap = 0;
67 #endif
68 int Most_Do_Regexp_Search = 0;
69
70 int Most_UTF8_Mode = -1; /* -1:auto, 0:off, 1:on */
71
72 static int Most_Starting_Line;
73 char *Most_Program; /* Program Name (argv[0]) */
74
75 static char *Most_Version = MOST_VERSION_STR;
76
77 #ifdef VMS
78 # ifndef isalpha
79 # define isalpha(x) \
80 (((x >= 'A') && (x <= 'Z'))||((x >= 'a') && (x <= 'z')) ? 1 : 0)
81 # endif
82 #endif
83
most_usage(void)84 void most_usage (void)
85 {
86 fprintf(stderr,"MOST version %s (S-Lang version %s)\n",
87 Most_Version, SLang_Version_String);
88 if (SLang_Version != SLANG_VERSION)
89 fprintf (stderr, " *Note: This executable was compiled against S-Lang %s\n", SLANG_VERSION_STRING);
90
91 fprintf (stderr, "Usage:\n");
92 fprintf(stderr, "most [-1Cbcdkrstvw] [+/string] [+line number] [+s] [+d] file...\n");
93 fputs(" where: -1: assume VT100 terminal. (VMS only)\n", stderr);
94 fputs(" -b: Startup in binary mode.\n", stderr);
95 fputs(" -C: disable color support\n", stderr);
96 fputs(" -c: Make searches case sensitive.\n", stderr);
97 fputs(" -d: Do not display the \\ wrap marker when wrapping lines.\n", stderr);
98 /* fputs(" -k: Kanji mode.\n", stderr); */
99 #if MOST_HAS_MMAP
100 fputs(" -M: Do not attempt to mmap files.\n", stderr);
101 #endif
102 fputs(" -r: Default to regexp search\n", stderr);
103 fputs(" -s: Squeeze out excess blank lines.\n", stderr);
104 fputs(" -t: Display tabs as ^I. If this option is immediately followed\n", stderr);
105 fputs(" by an integer, the integer sets the tab width.\n", stderr);
106 fputs(" -u: Disable UTF-8 mode\n", stderr);
107 fputs(" -v: Do not interpret backspace formatting characters.\n", stderr);
108 fputs(" -w: Wrap lines.\n", stderr);
109 fputs(" -z: No gunzip-on-the-fly.\n", stderr);
110 fputs(" +/string:\n", stderr);
111 fputs(" Search for string\n", stderr);
112 fputs(" +line number\n", stderr);
113 fputs(" Start up at specified line number.\n", stderr);
114 fputs(" +d: Allow file deletion.\n", stderr);
115 fputs(" +s: Secure Mode-- no edit, cd, shell, and reading files not\n", stderr);
116 fputs(" already listed on the command line.\n", stderr);
117 fputs(" +u: Enable UTF-8 mode.\n", stderr);
118 fprintf(stderr, "\nExample: most -ct4 +82 keymap.c\n");
119 fputs(" makes searches case sensitive, sets tabwidth to 4, and displays the file\n", stderr);
120 fputs(" keymap.c starting at line 82.\n", stderr);
121 }
122
123 static void do_switches(char *str);
124
do_extended_switches(char * str)125 static void do_extended_switches(char *str)
126 {
127 int i;
128 char ch;
129 char numstr [256];
130
131 i = 0;
132 ch = *(++str);
133 if ( ch == '/')
134 {
135 strcpy (Most_Search_Str,++str);
136 return;
137 }
138
139 if (ch >= '0' && ch <= '9')
140 {
141 while ((ch >= '0' && ch <= '9') && (i < 10))
142 {
143 numstr[i++] = ch;
144 ch = *(++str);
145 }
146 numstr[i] = '\0';
147 if (1 == sscanf (numstr,"%d", &i))
148 Most_Starting_Line = i;
149 return;
150 }
151
152 if (isalpha(ch))
153 {
154 while (1)
155 {
156 switch (ch)
157 {
158 case 0:
159 return;
160 case ' ':
161 case '+':
162 break;
163 case '-':
164 do_switches (str);
165 return;
166
167 case 'D':
168 case 'd':
169 Most_D_Opt = 1; /* delete file mode */
170 break;
171 case 'S':
172 case 's':
173 Most_Secure_Mode = 1;
174 break;
175
176 case 'U':
177 case 'u':
178 Most_UTF8_Mode = 1; /* +u */
179 break;
180
181 default:
182 fprintf(stderr,"%s invalid extended option %c ignored.\n",
183 Most_Program, ch);
184 }
185 ch = *(++str);
186 }
187 }
188
189 fprintf(stderr,"%s: switch '+%s' not valid.\n", Most_Program, str);
190 exit (1);
191 }
192
193 /* if non-zero, assume terminal is only a generic vt100 */
194 static int assume_vt100 = 0;
195 static int No_Colors = 0;
196
do_switches(char * str)197 static void do_switches(char *str)
198 {
199 char ch;
200 if (*str == '-') str++;
201 while ((ch = *str++) != '\0')
202 {
203 switch (ch)
204 {
205 default:
206 fprintf(stderr,"%s: invalid switch %c ignored.\n",
207 Most_Program, ch);
208 break;
209
210 case 'C':
211 No_Colors = 1;
212 break;
213 case 'c':
214 Most_Case_Sensitive = 1;
215 break;
216 case 'd':
217 case 'D':
218 Most_Show_Wrap_Marker = 0;
219 break;
220 case 'r':
221 Most_Do_Regexp_Search = 1;
222 break;
223 case 's':
224 case 'S':
225 Most_S_Opt = 1; break;
226 case 'V':
227 case 'v':
228 Most_V_Opt = 1; /* verbose-- convert control chars to '^' 'ch' */
229 break;
230 case 'W':
231 case 'w': Most_W_Opt = 1; break;
232
233 case 'K': /* Kanji option */
234 case 'k':
235 /* Most_K_Opt = 1; break; */
236 break;
237
238 case 'B':
239 case 'b':
240 Most_B_Opt = 1; /* Binary display 8 bit */
241 break;
242
243 case 'M':
244 #if MOST_HAS_MMAP
245 Most_Disable_MMap = 1;
246 #endif
247 break;
248
249 case 'z':
250 case 'Z':
251 Most_Z_Opt = 1; /* NO Gunzip-on-the-fly */
252 break;
253
254 case 't':
255 case 'T': /* expand tabs to '^I'; meaningful only with 'v' */
256 ch = *str;
257 if ((ch <= '9') && (ch >= '0'))
258 {
259 str++;
260 Most_Tab_Width = (int) ch - '0';
261 if (Most_Tab_Width == 0) Most_T_Opt = 1;
262 }
263 else Most_T_Opt = 1;
264 break;
265
266 case 'n': case 'N':
267 /* could be the Gopher Naive user switch --- ignored. */
268 break;
269 case '1': assume_vt100 = 1;
270 break;
271
272 case 'u':
273 case 'U':
274 Most_UTF8_Mode = 0; /* -u */
275 break;
276
277 /* Allow MOST_SWITCHES environment variable to contain + forms,
278 * e.g., "-sn+d" or "-s -n +d"
279 */
280 case ' ':
281 case '-':
282 break;
283 case '+':
284 do_extended_switches (str - 1); /* include '+' */
285 return;
286 }
287 }
288 }
289
most_exit_error(char * fmt,...)290 void most_exit_error(char *fmt,...)
291 {
292 va_list ap;
293
294 most_reset_tty ();
295 most_reset_display();
296 if (fmt != NULL)
297 {
298 va_start (ap, fmt);
299 vfprintf(stderr, fmt, ap);
300 va_end (ap);
301 putc ('\n', stderr);
302 }
303 #ifdef MALLOC_DEBUG
304 SLmalloc_dump_statistics ();
305 #endif
306 exit(1);
307 }
308
play_cat(char * file)309 static void play_cat(char *file)
310 {
311 char buf[4096 * 4];
312 int n;
313 FILE *fp;
314
315 if (file == NULL) fp = stdin;
316 else
317 {
318 fp = fopen(file, "r");
319 if (fp == NULL) return;
320 }
321
322 while ((n = fread(buf, 1, 4096 * 4, fp)) > 0)
323 {
324 int m;
325 m = fwrite (buf, 1, n, stdout);
326 if (m != n)
327 {
328 fprintf (stderr, "fwrite returned %d, errno = %d\n",
329 m, errno);
330 (void) fclose (fp);
331 exit (1);
332 }
333 }
334 (void) fclose (fp);
335 }
336
most_initialize_most(void)337 void most_initialize_most (void)
338 {
339 Most_S_Opt = 0;
340 Most_A_Opt = 1;
341 Most_V_Opt = 0;
342 Most_B_Opt = 0;
343 Most_T_Opt = 0;
344 Most_D_Opt = 0;
345 Most_K_Opt = 0;
346 Most_W_Opt = 0;
347
348 Most_Selective_Display = 0;
349 *Most_Search_Str = 0; Most_Search_Dir = 1;
350 Most_Top_Win = Most_Win = NULL;
351 Most_Buf = NULL;
352 Most_Eob = NULL;
353 Most_Beg = NULL;
354 Most_Captive_Mode = Most_Secure_Mode = 0;
355 Most_Want_Exit = 0;
356 }
357
do_most(char * file,int start)358 static void do_most (char *file, int start)
359 {
360 MOST_INT row, col;
361
362 most_get_cdir(Most_C_Dir);
363
364 row = col = 0;
365
366 if ((-1 == most_find_file (file))
367 && (Most_Num_Files == 1))
368 most_exit_error ("%s: failed to open for reading.", file);
369
370 most_init_display ();
371
372 most_goto_line(start);
373
374 Most_Curs_Offset = Most_C_Offset;
375
376 if (*Most_Search_Str
377 && ((row = most_search (Most_Beg + Most_C_Offset, 1, &col)) > 0))
378 most_goto_line(row);
379 else
380 {
381 row = Most_C_Line;
382 col = 1;
383 }
384
385 most_window_buffer();
386 Most_Curs_Row = Most_Win->curs_line = row - Most_C_Line + 1;
387 Most_Win->curs_offset = Most_Curs_Offset;
388 Most_Curs_Col = Most_Win->curs_col = col;
389 most_redraw_window();
390 most_update_status();
391
392 while (Most_Want_Exit == 0)
393 {
394 most_execute_key();
395 }
396 }
397
most_exit_most(void)398 void most_exit_most (void)
399 {
400 if (Most_Want_Exit) return;
401 Most_Want_Exit = 1;
402 most_clear_minibuffer ();
403 most_reset_tty ();
404 most_reset_display ();
405 most_free_windows ();
406 #ifdef MALLOC_DEBUG
407 SLmalloc_dump_statistics ();
408 #endif
409 }
410
utf8_config(void)411 static void utf8_config (void)
412 {
413 int utf8_mode = Most_UTF8_Mode;
414
415 utf8_mode = SLutf8_enable (-1); /* returns 0 or 1 */
416 if (Most_UTF8_Mode == -1)
417 Most_UTF8_Mode = utf8_mode;
418 else if (utf8_mode != Most_UTF8_Mode)
419 {
420 if (utf8_mode == 1)
421 (void) SLsmg_utf8_enable (0); /* locale is UTF-8, but -u passed */
422 else
423 (void) SLsmg_utf8_enable (1); /* locale not UTF-8, but +u passed */
424 }
425 }
426
most(int argc,char ** argv)427 int most (int argc, char **argv)
428 {
429 char file[MAX_PATHLEN], *switches;
430 int file_i = 0, quit,i,piped;
431 int status = 0;
432
433 #ifdef VMS
434 char filename[256];
435 #else
436 int j;
437 #endif
438
439 Most_Program = argv[0];
440 piped = 0;
441
442 switches = getenv ("MOST_PROMPT");
443 if ((switches != NULL) && (*switches != 0)) Most_Global_Msg = switches;
444
445 switches = getenv("MOST_SWITCHES");
446 if (switches != NULL) do_switches(switches);
447
448 i = 1;
449 if (argc > 1)
450 {
451 quit = 0;
452 while ((!quit) && (i < argc))
453 {
454 if (argv[i][0] == '-')
455 do_switches(argv[i++]);
456 else if (argv[i][0] == '+')
457 do_extended_switches(argv[i++]);
458 else quit = 1;
459 }
460 }
461
462 #if MOST_HAS_MMAP
463 /* if (Most_D_Opt) */
464 /* Most_Disable_MMap = 1; */
465 #endif
466
467 if (i == argc)
468 {
469 if (isatty(0)) /* 1 if stdin is a terminal, 0 otherwise */
470 {
471 most_usage ();
472 return 0;
473 }
474 /* assume input is from stdin */
475 file[0] = '\0'; /* tells most this is stdin */
476 piped = 1;
477 if (!isatty(fileno(stdout)))
478 {
479 play_cat(NULL);
480 return 0;
481 }
482 }
483 else
484 {
485 strncpy (file, argv[i], sizeof(file));
486 file[sizeof(file)-1] = 0;
487 }
488
489 if (!isatty(fileno(stdout)))
490 {
491 while (i < argc) play_cat(argv[i++]);
492 exit(0);
493 }
494
495 Most_Num_Files = 0;
496
497 SLtt_get_terminfo();
498 utf8_config ();
499 SLtt_Ignore_Beep = 1;
500 if (No_Colors)
501 SLtt_Use_Ansi_Colors = 0;
502
503 most_setup_colors ();
504 most_init_tty ();
505 most_init_keymaps ();
506
507 if (Most_B_Opt) Most_A_Opt = 0; /* explicit b overrides a */
508
509 if (!piped)
510 {
511 file_i = i;
512 #ifdef VMS
513 while(i < argc)
514 {
515 if (Most_Num_Files >= MOST_MAX_FILES) break;
516 if (argv[i][0] == '.') strcpy(file,"*"); else *file = 0;
517 strcat(file, most_unix2vms(argv[i++]));
518 while (RMS$_NORMAL == (status = most_expand_file_name(file,filename)))
519 {
520 Most_File_Ring[Most_Num_Files] = (char*) MOSTMALLOC(strlen(filename) + 1);
521 strcpy(Most_File_Ring[Most_Num_Files++], filename);
522 }
523 if (status == RMS$_NMF) status = RMS$_NORMAL; /* avoid spurious warning message */
524 }
525
526 if (Most_Num_Files) strcpy(file,Most_File_Ring[0]);
527 else fputs("%%MOST-W-NOFILES, no files found\n", stderr);
528 #else
529 Most_Num_Files = argc - i;
530 if (Most_Num_Files > MOST_MAX_FILES)
531 {
532 Most_Num_Files = MOST_MAX_FILES;
533 argc = Most_Num_Files + i;
534 }
535
536 j = 0;
537 while (i < argc)
538 {
539 Most_File_Ring[j++] = argv[i++];
540 }
541 #endif
542 }
543
544 if (Most_Num_Files || piped) do_most(file, Most_Starting_Line);
545 else if (Most_Num_Files == 0)
546 fprintf(stderr,"File %s not found\n", argv[file_i]);
547
548 most_exit_most ();
549 return status;
550 }
551
552 #if SLANG_VERSION <= 10409
553
SLang_set_error(int x)554 int SLang_set_error (int x)
555 {
556 SLang_Error = x;
557 return 0;
558 }
559
SLang_get_error(void)560 int SLang_get_error (void)
561 {
562 return SLang_Error;
563 }
564
565 #endif
566