1 /*
2  * GNU Typist  - interactive typing tutor program for UNIX systems
3  *
4  * Copyright (C) 1998, 1999, 2000, 2001, 2002, 2003
5  * 					Simon Baldwin (simonb@sco.com)
6  * Copyright (C) 2003  GNU Typist Development Team <bug-gtypist@gnu.org>
7  *
8  * This program is free software: you can redistribute it and/or modify
9  * it under the terms of the GNU General Public License as published by
10  * the Free Software Foundation, either version 3 of the License, or
11  * (at your option) any later version.
12  *
13  * This program is distributed in the hope that it will be useful,
14  * but WITHOUT ANY WARRANTY; without even the implied warranty of
15  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
16  * GNU General Public License for more details.
17  *
18  * You should have received a copy of the GNU General Public License
19  * along with this program.  If not, see <http://www.gnu.org/licenses/>.
20  */
21 #include "config.h"
22 #include "script.h"
23 
24 #ifdef HAVE_PDCURSES
25 #include <curses.h>
26 #else
27 #include <ncurses.h>
28 #endif
29 
30 #include "error.h"
31 
32 #include <stdlib.h>
33 #include <string.h>
34 #include <ctype.h>
35 #include <utf8.h>
36 
37 #include "gettext.h"
38 #define _(String) gettext (String)
39 
40 extern int isUTF8Locale;
41 
42 int global_line_counter = 0;
43 struct label_entry *global_label_list[NLHASH];
44 
45 // Nobody bothers to free this string at exit.
46 char *__last_label = NULL;
47 
__update_last_label(const char * label)48 void __update_last_label (const char *label)
49 {
50   if (__last_label)
51      free (__last_label);
52   __last_label = strdup (label);
53   if (!__last_label)
54   {
55      perror ("strdup");
56      fatal_error (_("internal error: strdup"), label);
57   }
58 }
59 
60 /*
61   label hashing function - returns an index for the lists
62 */
63 int
hash_label(char * label)64 hash_label( char *label ) {
65   char	*p;				/* pointer through string */
66   int	csum = 0;			/* sum of characters */
67 
68   /* hash by summing the characters and taking modulo of the
69      number of hash lists defined */
70   for ( p = label; *p != ASCII_NULL; p++ )
71     csum += *p;
72   return ( csum % NLHASH );
73 }
74 
line_is_empty(const char * line)75 static int line_is_empty (const char *line)
76 {
77    while (*line)
78    {
79       if (!isspace (*line))
80          return 0;
81 
82       line ++;
83    }
84 
85    return 1;
86 }
87 
88 /*
89   go through the file and convert it from UTF8, to make
90   sure that all characters can be converted!
91 */
check_script_file_with_current_encoding(FILE * script)92 void check_script_file_with_current_encoding( FILE *script )
93 {
94     char line[MAX_SCR_LINE];
95 
96     get_script_line( script, line );
97     while (! feof( script ))
98     {
99         wchar_t* lineConverted = convertFromUTF8(line);
100         free(lineConverted);
101         /* get the next script line */
102         get_script_line( script, line );
103     }
104 }
105 
106 /*
107   go through the file, index all the labels we can find and notice if
108   we have a menu or not
109 */
build_label_index(FILE * script)110 void build_label_index( FILE *script ) {
111   char line[MAX_SCR_LINE];
112   char *line_iterator;
113   struct label_entry	*new_label = NULL;	/* new label entry */
114   struct label_entry	*list_tail[NLHASH];	/* tail tracking */
115   int	hash;					/* hash index */
116   struct label_entry	*check_label;		/* pointer thru list */
117 
118   /* start at the file's beginning */
119   rewind( script );
120   global_line_counter = 0;
121 
122   /* initialize the hash lists */
123   for ( hash = 0; hash < NLHASH; hash++ )
124     {
125       global_label_list[ hash ] = NULL;
126       list_tail[ hash ] = NULL;
127     }
128 
129   /* read until we get to eof */
130   get_script_line( script, line );
131   while (! feof( script ))
132     {
133 
134       /* see if this is a label line */
135       if ( SCR_COMMAND( line ) == C_LABEL )
136 	{
137 
138 	  /* note this label's position in the table;
139 	     first, create a new list entry */
140 	  new_label = malloc( sizeof(struct label_entry) );
141 	  if ( new_label == NULL )
142 	    fatal_error( _("internal error: malloc"), line );
143 
144 	  /* remove trailing whitespace from line */
145 	  line_iterator = line + strlen(line) - 1;
146 	  while (line_iterator != line && isspace(*line_iterator))
147 	    {
148 	      *line_iterator = '\0';
149 	      --line_iterator;
150 	    }
151 
152 	  /* check for spaces in the label (these are banned since 2.9 so that
153 	     spaces can be used as field separators in the bestlog) */
154 	  line_iterator = SCR_DATA( line );
155 	  while( *line_iterator != '\0' )
156 	    {
157 	      if( *line_iterator == ' ' )
158 		fatal_error( _("label contains space"), line );
159 	      ++line_iterator;
160 	    }
161 
162 	  /* make some space for the label string */
163 	  new_label->label =
164 	    malloc( strlen( SCR_DATA( line )) + 1 );
165 	  if ( new_label->label == NULL )
166 	    fatal_error( _("internal error: malloc"), line );
167 
168 	  /* copy the data into the new structure */
169 	  strcpy( new_label->label, SCR_DATA( line ));
170 	  new_label->offset = ftell( script );
171 	  new_label->line_count = global_line_counter;
172 	  new_label->next = NULL;
173 
174 	  /* find the right hash list for the label */
175 	  hash = hash_label( SCR_DATA( line ));
176 
177 	  /* search the linked list for the label, to
178 	     see if it's already there - nice to check */
179 	  for ( check_label = global_label_list[ hash ];
180 		check_label != NULL;
181 		check_label = check_label->next )
182 	    {
183 	      if ( strcmp( check_label->label, SCR_DATA( line )) == 0 )
184 		fatal_error( _("label redefinition"), line );
185 	    }
186 
187 	  /* link everything together */
188 	  if ( list_tail[ hash ] == NULL )
189 	    global_label_list[ hash ] = new_label;
190 	  else
191 	    list_tail[ hash ]->next = new_label;
192 	  list_tail[ hash ] = new_label;
193 	}
194 
195       /* get the next script line */
196       get_script_line( script, line );
197     }
198 }
199 
200 /*
201   get the next non-comment, non-blank line from the script file
202   and check its basic format
203 */
get_script_line(FILE * script,char * line)204 void get_script_line( FILE *script, char *line )
205 {
206   /* get lines until not empty/comment, or eof found */
207   fgets(line, MAX_SCR_LINE, script);
208   global_line_counter++;
209   while (! feof (script) &&
210 	   (line_is_empty (line) ||
211 	    SCR_COMMAND (line) == C_COMMENT ||
212 	    SCR_COMMAND (line) == C_ALT_COMMENT))
213     {
214       fgets(line, MAX_SCR_LINE, script);
215       global_line_counter++;
216     }
217 
218   /* if a line was read then check it */
219   if ( ! feof( script ))
220     {
221       /* Get rid of trailing spaces and newline */
222       while( *line && isspace( line[strlen( line )-1] ) )
223         line [strlen (line) - 1] = ASCII_NULL;
224 
225       // input is UTF-8 !!
226       int numChars = utf8len(line);
227       if (numChars == -1)
228         fatal_error( _("Invalid multibyte sequence (wrong encoding?)"), line);
229       if ( numChars < MIN_SCR_LINE )
230 	fatal_error( _("data shortage"), line );
231       if ( SCR_SEP( line ) != C_SEP )
232 	fatal_error( _("missing ':'"), line );
233       if ( SCR_COMMAND( line ) != C_LABEL
234 	   && SCR_COMMAND( line ) != C_GOTO
235 	   && SCR_COMMAND( line ) != C_YGOTO
236 	   && SCR_COMMAND( line ) != C_NGOTO
237            && utf8len(SCR_DATA( line )) > COLS )
238 	fatal_error( _("line too long for screen"), line );
239     }
240 }
241 
242 /*
243   buffer up the complete data from a command; used for [Dd], [Pp] and M
244 */
buffer_command(FILE * script,char * line)245 char *buffer_command( FILE *script, char *line ) {
246   int	total_chars = 0;		/* character counter */
247   char	*data = NULL;			/* data string */
248 
249   /* get the complete exercise into a single string */
250   do
251     {
252       data = (char*)realloc( data, (data ? strlen( data ) : 0) +
253 			       strlen(SCR_DATA( line )) +
254 			       strlen(STRING_NL) + 1 );
255       if ( data == NULL )
256 	fatal_error( _("internal error: malloc"), line );
257 
258       /* store the data in the allocated area */
259       if ( total_chars == 0 )
260 	strcpy( data, "" );
261       strcat( data, SCR_DATA( line ) );
262       strcat( data, STRING_NL );
263       total_chars = strlen( data );
264 
265       /* and get the next script line */
266       get_script_line( script, line );
267     }
268   while ( SCR_COMMAND( line ) == C_CONT && ! feof( script ));
269 
270   /* return our (malloced) data */
271   return( data );
272 }
273 
274 /*
275   search out a label from the file, and set the file pointer to
276   that location
277 
278   It also updates __last_label for the returns into menu from lessons.
279 */
280 void
seek_label(FILE * script,char * label,char * ref_line)281 seek_label( FILE *script, char *label, char *ref_line ) {
282   struct label_entry	*check_label;	/* pointer through list */
283   int	hash;				/* hash index */
284   char	err[MAX_SCR_LINE];		/* error message string */
285 
286   if (!label)
287      do_exit (script);
288 
289   __update_last_label (label);
290 
291   /* find the right hash list for the label */
292   hash = hash_label( label );
293 
294   /* search the linked list for the label */
295   for ( check_label = global_label_list[ hash ]; check_label != NULL;
296 	check_label = check_label->next )
297     {
298 
299       /* see if this is our label */
300       if ( strcmp( check_label->label, label ) == 0 )
301 	break;
302     }
303 
304   /* see if the label was not found in the file */
305   if ( check_label == NULL )
306     {
307       sprintf( err, _("label '%s' not found"), label );
308       fatal_error( err, ref_line );
309     }
310 
311   /* move to the label position in the file */
312   if ( fseek( script, check_label->offset, SEEK_SET ) == -1 )
313     fatal_error( _("internal error: fseek"), ref_line );
314   global_line_counter = check_label->line_count;
315 }
316 
317 /*
318   exit from the program (implied on eof)
319 */
320 void
do_exit(FILE * script)321 do_exit( FILE *script )
322 {
323   /* close up all files, reset the screen stuff, and exit */
324   fclose( script );
325   /* if ( cl_colour && has_colors() )*/
326   wbkgdset( stdscr, 0 );
327   clear(); refresh(); endwin();
328   if (isUTF8Locale)
329   {
330       printf(_("Happy Typing!\n\n"));
331   }
332   else
333   {
334       printf("%s", convertUTF8ToCurrentEncoding(_("Happy Typing!\n\n")));
335   }
336   exit( 0 );
337 }
338 
339 /*
340   Local Variables:
341   tab-width: 8
342   End:
343 */
344