Update to less-418:
[dragonfly.git] / contrib / less-394 / main.c
1 /*
2  * Copyright (C) 1984-2004  Mark Nudelman
3  *
4  * You may distribute under the terms of either the GNU General Public
5  * License or the Less License, as specified in the README file.
6  *
7  * For more information about less, or for information on how to 
8  * contact the author, see the README file.
9  */
10
11
12 /*
13  * Entry point, initialization, miscellaneous routines.
14  */
15
16 #include "less.h"
17 #if MSDOS_COMPILER==WIN32C
18 #include <windows.h>
19 #endif
20
21 public char *   every_first_cmd = NULL;
22 public int      new_file;
23 public int      is_tty;
24 public IFILE    curr_ifile = NULL_IFILE;
25 public IFILE    old_ifile = NULL_IFILE;
26 public struct scrpos initial_scrpos;
27 public int      any_display = FALSE;
28 public POSITION start_attnpos = NULL_POSITION;
29 public POSITION end_attnpos = NULL_POSITION;
30 public int      wscroll;
31 public char *   progname;
32 public int      quitting;
33 public int      secure;
34 public int      dohelp;
35
36 #if LOGFILE
37 public int      logfile = -1;
38 public int      force_logfile = FALSE;
39 public char *   namelogfile = NULL;
40 #endif
41
42 #if EDITOR
43 public char *   editor;
44 public char *   editproto;
45 #endif
46
47 #if TAGS
48 extern char *   tags;
49 extern char *   tagoption;
50 extern int      jump_sline;
51 #endif
52
53 #ifdef WIN32
54 static char consoleTitle[256];
55 #endif
56
57 extern int      missing_cap;
58 extern int      know_dumb;
59
60
61 /*
62  * Entry point.
63  */
64 int
65 main(argc, argv)
66         int argc;
67         char *argv[];
68 {
69         IFILE ifile;
70         char *s;
71
72 #ifdef __EMX__
73         _response(&argc, &argv);
74         _wildcard(&argc, &argv);
75 #endif
76
77         progname = *argv++;
78         argc--;
79
80         secure = 0;
81         s = lgetenv("LESSSECURE");
82         if (s != NULL && *s != '\0')
83                 secure = 1;
84
85 #ifdef WIN32
86         if (getenv("HOME") == NULL)
87         {
88                 /*
89                  * If there is no HOME environment variable,
90                  * try the concatenation of HOMEDRIVE + HOMEPATH.
91                  */
92                 char *drive = getenv("HOMEDRIVE");
93                 char *path  = getenv("HOMEPATH");
94                 if (drive != NULL && path != NULL)
95                 {
96                         char *env = (char *) ecalloc(strlen(drive) + 
97                                         strlen(path) + 6, sizeof(char));
98                         strcpy(env, "HOME=");
99                         strcat(env, drive);
100                         strcat(env, path);
101                         putenv(env);
102                 }
103         }
104         GetConsoleTitle(consoleTitle, sizeof(consoleTitle)/sizeof(char));
105 #endif /* WIN32 */
106
107         /*
108          * Process command line arguments and LESS environment arguments.
109          * Command line arguments override environment arguments.
110          */
111         is_tty = isatty(1);
112         get_term();
113         init_cmds();
114         init_prompt();
115         init_charset();
116         init_line();
117         init_cmdhist();
118         init_option();
119         s = lgetenv("LESS");
120         if (s != NULL)
121                 scan_option(save(s));
122
123 #define isoptstring(s)  (((s)[0] == '-' || (s)[0] == '+') && (s)[1] != '\0')
124         while (argc > 0 && (isoptstring(*argv) || isoptpending()))
125         {
126                 s = *argv++;
127                 argc--;
128                 if (strcmp(s, "--") == 0)
129                         break;
130                 scan_option(s);
131         }
132 #undef isoptstring
133
134         if (isoptpending())
135         {
136                 /*
137                  * Last command line option was a flag requiring a
138                  * following string, but there was no following string.
139                  */
140                 nopendopt();
141                 quit(QUIT_OK);
142         }
143
144 #if EDITOR
145         editor = lgetenv("VISUAL");
146         if (editor == NULL || *editor == '\0')
147         {
148                 editor = lgetenv("EDITOR");
149                 if (editor == NULL || *editor == '\0')
150                         editor = EDIT_PGM;
151         }
152         editproto = lgetenv("LESSEDIT");
153         if (editproto == NULL || *editproto == '\0')
154                 editproto = "%E ?lm+%lm. %f";
155 #endif
156
157         /*
158          * Call get_ifile with all the command line filenames
159          * to "register" them with the ifile system.
160          */
161         ifile = NULL_IFILE;
162         if (dohelp)
163                 ifile = get_ifile(FAKE_HELPFILE, ifile);
164         while (argc-- > 0)
165         {
166                 char *filename;
167 #if (MSDOS_COMPILER && MSDOS_COMPILER != DJGPPC)
168                 /*
169                  * Because the "shell" doesn't expand filename patterns,
170                  * treat each argument as a filename pattern rather than
171                  * a single filename.  
172                  * Expand the pattern and iterate over the expanded list.
173                  */
174                 struct textlist tlist;
175                 char *gfilename;
176                 
177                 gfilename = lglob(*argv++);
178                 init_textlist(&tlist, gfilename);
179                 filename = NULL;
180                 while ((filename = forw_textlist(&tlist, filename)) != NULL)
181                 {
182                         (void) get_ifile(filename, ifile);
183                         ifile = prev_ifile(NULL_IFILE);
184                 }
185                 free(gfilename);
186 #else
187                 filename = shell_quote(*argv);
188                 if (filename == NULL)
189                         filename = *argv;
190                 argv++;
191                 (void) get_ifile(filename, ifile);
192                 ifile = prev_ifile(NULL_IFILE);
193 #endif
194         }
195         /*
196          * Set up terminal, etc.
197          */
198         if (!is_tty)
199         {
200                 /*
201                  * Output is not a tty.
202                  * Just copy the input file(s) to output.
203                  */
204                 SET_BINARY(1);
205                 if (nifile() == 0)
206                 {
207                         if (edit_stdin() == 0)
208                                 cat_file();
209                 } else if (edit_first() == 0)
210                 {
211                         do {
212                                 cat_file();
213                         } while (edit_next(1) == 0);
214                 }
215                 quit(QUIT_OK);
216         }
217
218         if (missing_cap && !know_dumb)
219                 error("WARNING: terminal is not fully functional", NULL_PARG);
220         init_mark();
221         open_getchr();
222         raw_mode(1);
223         init_signals(1);
224
225         /*
226          * Select the first file to examine.
227          */
228 #if TAGS
229         if (tagoption != NULL || strcmp(tags, "-") == 0)
230         {
231                 /*
232                  * A -t option was given.
233                  * Verify that no filenames were also given.
234                  * Edit the file selected by the "tags" search,
235                  * and search for the proper line in the file.
236                  */
237                 if (nifile() > 0)
238                 {
239                         error("No filenames allowed with -t option", NULL_PARG);
240                         quit(QUIT_ERROR);
241                 }
242                 findtag(tagoption);
243                 if (edit_tagfile())  /* Edit file which contains the tag */
244                         quit(QUIT_ERROR);
245                 /*
246                  * Search for the line which contains the tag.
247                  * Set up initial_scrpos so we display that line.
248                  */
249                 initial_scrpos.pos = tagsearch();
250                 if (initial_scrpos.pos == NULL_POSITION)
251                         quit(QUIT_ERROR);
252                 initial_scrpos.ln = jump_sline;
253         } else
254 #endif
255         if (nifile() == 0)
256         {
257                 if (edit_stdin())  /* Edit standard input */
258                         quit(QUIT_ERROR);
259         } else 
260         {
261                 if (edit_first())  /* Edit first valid file in cmd line */
262                         quit(QUIT_ERROR);
263         }
264
265         init();
266         commands();
267         quit(QUIT_OK);
268         /*NOTREACHED*/
269         return (0);
270 }
271
272 /*
273  * Copy a string to a "safe" place
274  * (that is, to a buffer allocated by calloc).
275  */
276         public char *
277 save(s)
278         char *s;
279 {
280         register char *p;
281
282         p = (char *) ecalloc(strlen(s)+1, sizeof(char));
283         strcpy(p, s);
284         return (p);
285 }
286
287 /*
288  * Allocate memory.
289  * Like calloc(), but never returns an error (NULL).
290  */
291         public VOID_POINTER
292 ecalloc(count, size)
293         int count;
294         unsigned int size;
295 {
296         register VOID_POINTER p;
297
298         p = (VOID_POINTER) calloc(count, size);
299         if (p != NULL)
300                 return (p);
301         error("Cannot allocate memory", NULL_PARG);
302         quit(QUIT_ERROR);
303         /*NOTREACHED*/
304         return (NULL);
305 }
306
307 /*
308  * Skip leading spaces in a string.
309  */
310         public char *
311 skipsp(s)
312         register char *s;
313 {
314         while (*s == ' ' || *s == '\t') 
315                 s++;
316         return (s);
317 }
318
319 /*
320  * See how many characters of two strings are identical.
321  * If uppercase is true, the first string must begin with an uppercase
322  * character; the remainder of the first string may be either case.
323  */
324         public int
325 sprefix(ps, s, uppercase)
326         char *ps;
327         char *s;
328         int uppercase;
329 {
330         register int c;
331         register int sc;
332         register int len = 0;
333
334         for ( ;  *s != '\0';  s++, ps++)
335         {
336                 c = *ps;
337                 if (uppercase)
338                 {
339                         if (len == 0 && ASCII_IS_LOWER(c))
340                                 return (-1);
341                         if (ASCII_IS_UPPER(c))
342                                 c = ASCII_TO_LOWER(c);
343                 }
344                 sc = *s;
345                 if (len > 0 && ASCII_IS_UPPER(sc))
346                         sc = ASCII_TO_LOWER(sc);
347                 if (c != sc)
348                         break;
349                 len++;
350         }
351         return (len);
352 }
353
354 /*
355  * Exit the program.
356  */
357         public void
358 quit(status)
359         int status;
360 {
361         static int save_status;
362
363         /*
364          * Put cursor at bottom left corner, clear the line,
365          * reset the terminal modes, and exit.
366          */
367         if (status < 0)
368                 status = save_status;
369         else
370                 save_status = status;
371         quitting = 1;
372         edit((char*)NULL);
373         save_cmdhist();
374         if (any_display && is_tty)
375                 clear_bot();
376         deinit();
377         flush();
378         raw_mode(0);
379 #if MSDOS_COMPILER && MSDOS_COMPILER != DJGPPC
380         /* 
381          * If we don't close 2, we get some garbage from
382          * 2's buffer when it flushes automatically.
383          * I cannot track this one down  RB
384          * The same bug shows up if we use ^C^C to abort.
385          */
386         close(2);
387 #endif
388 #if WIN32
389         SetConsoleTitle(consoleTitle);
390 #endif
391         close_getchr();
392         exit(status);
393 }