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