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