2 * src/fstream.c - type representing the tesh file stream.
\r
4 * Copyright 2008,2009 Martin Quinson, Malek Cherier All right reserved.
\r
6 * This program is free software; you can redistribute it and/or modify it
\r
7 * under the terms of the license (GNU LGPL) which comes with this package.
\r
10 * This file contains all the definitions of the functions related with
\r
11 * the tesh file stream type.
\r
15 #include <fstream.h>
\r
17 #include <context.h>
\r
18 #include <command.h>
\r
20 #include <str_replace.h>
\r
21 #include <variable.h>
\r
25 XBT_LOG_EXTERNAL_DEFAULT_CATEGORY(tesh);
\r
28 failure(unit_t unit)
\r
31 if(!keep_going_unit_flag)
\r
33 unit_t root = unit->root ? unit->root : unit;
\r
35 if(!root->interrupted)
\r
37 /* the unit interrupted (exit for the loop) */
\r
38 root->interrupted = 1;
\r
40 /* release the unit */
\r
41 xbt_os_sem_release(root->sem);
\r
44 /* if the --keep-going option is not specified */
\r
45 if(!keep_going_flag)
\r
49 /* request an global interruption by the runner */
\r
52 /* release the runner */
\r
53 xbt_os_sem_release(units_sem);
\r
60 syntax_error(unit_t unit)
\r
62 error_register("Unit failure", ESYNTAX, NULL, unit->fstream->name);
\r
69 internal_error(unit_t unit)
\r
75 sys_error(unit_t unit)
\r
79 error_register("System error", errno, NULL, unit->fstream->name);
\r
81 root = unit->root ? unit->root : unit;
\r
83 if(!root->interrupted)
\r
85 /* the unit interrupted (exit for the loop) */
\r
86 root->interrupted = 1;
\r
88 /* release the unit */
\r
89 xbt_os_sem_release(root->sem);
\r
94 /* request an global interruption by the runner */
\r
97 /* release the runner */
\r
98 xbt_os_sem_release(units_sem);
\r
103 fstream_new(const char* directory, const char* name)
\r
113 if(!directory && !strcmp("stdin", name))
\r
115 fstream = xbt_new0(s_fstream_t, 1);
\r
116 fstream->name = strdup("stdin");
\r
119 else if(!directory)
\r
125 fstream = xbt_new0(s_fstream_t, 1);
\r
127 if(!(fstream->name = strdup(name)))
\r
133 if(!(fstream->directory = strdup(directory)))
\r
135 free(fstream->name);
\r
140 fstream->stream = NULL;
\r
141 fstream->unit = NULL;
\r
148 fstream_open(fstream_t fstream)
\r
150 char path[PATH_MAX + 1] = {0};
\r
152 /* check the parameter */
\r
159 if(!fstream || fstream->stream)
\r
165 if(!strcmp(fstream->name, "stdin"))
\r
167 fstream->stream = stdin;
\r
172 sprintf(path,"%s/%s",fstream->directory, fstream->name);
\r
174 sprintf(path,"%s\\%s",fstream->directory, fstream->name);
\r
177 if(!(fstream->stream = fopen(path, "r")))
\r
184 fstream_close(fstream_t fstream)
\r
186 /* check the parameter */
\r
187 if(!(fstream) || !strcmp(fstream->name, "stdin") )
\r
193 if(!fstream->stream)
\r
196 if(EOF == fclose(fstream->stream))
\r
199 fstream->stream = NULL;
\r
205 fstream_free(fstream_t* ptr)
\r
208 /* check the parameter */
\r
219 fclose((*ptr)->stream);
\r
221 free((*ptr)->name);
\r
223 if((*ptr)->directory)
\r
224 free((*ptr)->directory);
\r
235 fstream_parse(fstream_t fstream, xbt_os_mutex_t mutex)
\r
238 char * line = NULL;
\r
240 char file_pos[256];
\r
241 xbt_strbuff_t buff;
\r
242 int buffbegin = 0;
\r
246 /* Count the line length while checking wheather it's blank */
\r
249 /* Deal with \ at the end of the line, and call handle_line on result */
\r
250 int to_be_continued;
\r
252 /* check the parameter */
\r
253 if(!(fstream) || !mutex)
\r
259 buff = xbt_strbuff_new();
\r
261 if(!(context = context_new()))
\r
264 unit = fstream->unit;
\r
266 while(!(unit->root->interrupted) && getline(&line, &len, fstream->stream) != -1)
\r
271 to_be_continued = 0;
\r
275 while(line[linelen] != '\0')
\r
277 if (line[linelen] != ' ' && line[linelen] != '\t' && line[linelen]!='\n' && line[linelen]!='\r')
\r
285 if(!context->command_line && (context->input->used || context->output->used))
\r
287 ERROR1("[%d] Error: no command found in this chunk of lines.",buffbegin);
\r
288 syntax_error(unit);
\r
291 else if(unit->is_running_suite)
\r
292 {/* it's the end of a suite */
\r
293 unit->is_running_suite = 0;
\r
296 if(context->command_line)
\r
298 if(fstream_launch_command(fstream, context, mutex) < 0)
\r
305 if(linelen>1 && line[linelen-2]=='\\')
\r
307 if(linelen>2 && line[linelen-3] == '\\')
\r
309 /* Damn. Escaped \ */
\r
310 line[linelen-2] = '\n';
\r
311 line[linelen-1] = '\0';
\r
315 to_be_continued = 1;
\r
316 line[linelen-2] = '\0';
\r
320 buffbegin = line_num;
\r
324 if(buff->used || to_be_continued)
\r
326 xbt_strbuff_append(buff,line);
\r
328 if (!to_be_continued)
\r
330 snprintf(file_pos,256,"%s:%d",fstream->name, buffbegin);
\r
331 fstream_lex_line(fstream, context, mutex, file_pos, buff->data);
\r
332 xbt_strbuff_empty(buff);
\r
337 snprintf(file_pos,256,"%s:%d",fstream->name, line_num);
\r
338 fstream_lex_line(fstream, context, mutex, file_pos, line);
\r
342 /* Check that last command of the file ran well */
\r
343 if(context->command_line)
\r
345 if(fstream_launch_command(fstream, context, mutex) < 0)
\r
349 /* clear buffers */
\r
353 xbt_strbuff_free(buff);
\r
355 if(context_free(&context) < 0)
\r
358 return (exit_code || errno) ? -1 : 0;
\r
363 fstream_lex_line(fstream_t fstream, context_t context, xbt_os_mutex_t mutex, const char * filepos, char *line)
\r
366 variable_t variable;
\r
368 char name[VAR_NAME_MAX + 1] = {0};
\r
369 unit_t unit = fstream->unit;
\r
370 xbt_dynar_t variables = unit->runner->variables;
\r
373 xbt_str_rtrim(line + 2,"\n");
\r
375 line2 = strdup(line);
\r
377 /* replace each variable by its value */
\r
378 xbt_os_mutex_acquire(unit->mutex);
\r
380 xbt_dynar_foreach(variables, i, variable)
\r
382 sprintf(name, "$%s", variable->name);
\r
383 str_replace_all(&line2, name, variable->val);
\r
384 memset(name, 0, VAR_NAME_MAX + 1);
\r
387 xbt_os_mutex_release(unit->mutex);
\r
397 context->async = (line2[0] == '&');
\r
399 /* further trim useless chars which are significant for in/output */
\r
400 xbt_str_rtrim(line2 + 2," \t");
\r
402 /* deal with CD commands here, not in context */
\r
403 if(!strncmp("cd ",line2 + 2, 3))
\r
405 char* dir = strdup(line2 + 4);
\r
407 if(context->command_line)
\r
409 if(fstream_launch_command(fstream, context, mutex) < 0)
\r
413 /* search begining */
\r
414 while(*(dir++) == ' ');
\r
421 INFO1("tesh cd %s",dir);
\r
423 if(!just_print_flag)
\r
427 ERROR1("chdir failed to set the directory to %s", dir);
\r
428 error_register("Unit failure", errno, NULL, unit->fstream->name);
\r
438 fstream_process_token(fstream, context, mutex, filepos, line2[0], line2 + 2);
\r
445 fstream_process_token(fstream, context, mutex, filepos, line2[0], line2 + 2);
\r
450 INFO2("[%s] %s",filepos,line2+2);
\r
455 CRITICAL2("[%s] %s",filepos,line2+2);
\r
459 if(unit->description)
\r
460 WARN2("Description already specified %s at %s", line2, filepos);
\r
462 unit->description = strdup(line2 + 2);
\r
466 error_register("Unit failure", errno, NULL, unit->fstream->name);
\r
467 syntax_error(unit);
\r
475 fstream_process_token(fstream_t fstream, context_t context, xbt_os_mutex_t mutex, const char* filepos, char token, char *line)
\r
477 unit_t unit = fstream->unit;
\r
484 if(context->command_line)
\r
487 if(context->output->used || context->input->used)
\r
489 ERROR2("[%s] More than one command in this chunk of lines (previous: %s).\nDunno which input/output belongs to which command.",filepos,context->command_line);
\r
490 syntax_error(unit);
\r
494 if(fstream_launch_command(fstream, context, mutex) < 0)
\r
497 VERB1("[%s] More than one command in this chunk of lines",filepos);
\r
500 context->command_line = strdup(line);
\r
501 context->line = strdup(filepos);
\r
507 xbt_strbuff_append(context->input,line);
\r
508 xbt_strbuff_append(context->input,"\n");
\r
512 xbt_strbuff_append(context->output,line);
\r
513 xbt_strbuff_append(context->output,"\n");
\r
518 if(context->command_line)
\r
519 if(fstream_launch_command(fstream, context, mutex) < 0)
\r
522 if(!strncmp(line,"timeout no",strlen("timeout no")))
\r
524 VERB1("[%s] (disable timeout)", filepos);
\r
525 context->timeout = INDEFINITE;
\r
527 else if(!strncmp(line,"timeout ",strlen("timeout ")))
\r
530 char* p = line + strlen("timeout ");
\r
532 while(p[i] != '\0')
\r
536 ERROR2("Invalid timeout value `%s' at %s ", line + strlen("timeout "), filepos);
\r
537 syntax_error(unit);
\r
544 context->timeout = atoi(line + strlen("timeout"));
\r
545 VERB2("[%s] (new timeout value: %d)",filepos,context->timeout);
\r
548 else if (!strncmp(line,"expect signal ",strlen("expect signal ")))
\r
550 context->signal = strdup(line + strlen("expect signal "));
\r
553 if(!strstr("SIGSEGVSIGTRAPSIGBUSSIGFPESIGILL", context->signal))
\r
555 ERROR2("Incompatible signal \'%s\' detected at %s", context->signal, filepos);
\r
556 syntax_error(unit);
\r
560 xbt_str_trim(context->signal," \n");
\r
561 VERB2("[%s] (next command must raise signal %s)", filepos, context->signal);
\r
564 else if (!strncmp(line,"expect return ",strlen("expect return ")))
\r
568 char* p = line + strlen("expect return ");
\r
570 while(p[i] != '\0')
\r
574 ERROR2("Invalid exit code value `%s' at %s ", line + strlen("expect return "), filepos);
\r
575 syntax_error(unit);
\r
581 context->exit_code = atoi(line+strlen("expect return "));
\r
582 VERB2("[%s] (next command must return code %d)",filepos, context->exit_code);
\r
585 else if (!strncmp(line,"output ignore",strlen("output ignore")))
\r
587 context->output_handling = oh_ignore;
\r
588 VERB1("[%s] (ignore output of next command)", filepos);
\r
591 else if (!strncmp(line,"output display",strlen("output display")))
\r
593 context->output_handling = oh_display;
\r
594 VERB1("[%s] (ignore output of next command)", filepos);
\r
597 else if(!strncmp(line,"include ", strlen("include ")))
\r
602 p1 = line + strlen("include");
\r
604 while(*p1 == ' ' || *p1 == '\t')
\r
610 ERROR1("include file not specified %s ", filepos);
\r
611 syntax_error(unit);
\r
615 char file_name[PATH_MAX + 1] = {0};
\r
619 while(*p2 != '\0' && *p2 != ' ' && *p2 != '\t')
\r
622 strncpy(file_name, p1, p2 - p1);
\r
626 while(*p2 == ' ' || *p2 == '\t')
\r
629 fstream_handle_include(fstream, context, mutex, file_name, p2[0] != '\0' ? p2 : NULL);
\r
633 else if(!strncmp(line,"suite ", strlen("suite ")))
\r
635 if(unit->is_running_suite)
\r
637 ERROR1("Suite already in process at %s", filepos);
\r
638 syntax_error(unit);
\r
642 fstream_handle_suite(fstream, context, mutex, line + strlen("suite "));
\r
644 else if(!strncmp(line,"unsetenv ", strlen("unsetenv ")))
\r
649 variable_t variable;
\r
650 char* name = line + strlen("unsetenv ");
\r
652 xbt_os_mutex_acquire(unit->mutex);
\r
655 xbt_dynar_foreach(unit->runner->variables, i, variable)
\r
657 if(!strcmp(variable->name, name))
\r
659 env = variable->env;
\r
668 /*xbt_dynar_remove_at(unit->runner->variables, i - 1, NULL);*/
\r
669 xbt_dynar_cursor_rm(unit->runner->variables, &i);
\r
671 WARN3("environment variable %s not found %s %s at", name, line, filepos);
\r
676 WARN3("%s is an not environment variable use unset metacommand to delete it %s %s", name, line, filepos);
\r
678 WARN3("%s environment variable not found %s %s", name, line, filepos);
\r
681 xbt_os_mutex_release(unit->mutex);
\r
685 else if(!strncmp(line,"setenv ", strlen("setenv ")))
\r
688 char name[PATH_MAX + 1] = {0};
\r
692 p = line + strlen("setenv ");
\r
694 val = strchr(p, '=');
\r
698 variable_t variable;
\r
706 ERROR2("Indefinite variable value %s %s", line, filepos);
\r
707 syntax_error(unit);
\r
712 strncpy(name, p, (val - p -1));
\r
714 /* test if the variable is already registred */
\r
715 xbt_os_mutex_acquire(unit->mutex);
\r
717 xbt_dynar_foreach(unit->runner->variables, i, variable)
\r
719 if(!strcmp(variable->name, name))
\r
727 /* if the variable is already registred, update its value;
\r
728 * otherwise register it.
\r
734 free(variable->val);
\r
735 variable->val = strdup(val);
\r
737 SetEnvironmentVariable(variable->name, variable->val);
\r
739 setenv(variable->name, variable->val, 1);
\r
743 WARN3("%s variable already exists %s %s", name, line, filepos);
\r
747 variable = variable_new(name, val);
\r
750 xbt_dynar_push(unit->runner->variables, &variable);
\r
753 SetEnvironmentVariable(variable->name, variable->val);
\r
755 setenv(variable->name, variable->val, 0);
\r
759 xbt_os_mutex_release(unit->mutex);
\r
763 else if(!strncmp(line,"unset ", strlen("unset ")))
\r
769 variable_t variable;
\r
770 char* name = line + strlen("unset ");
\r
772 xbt_os_mutex_acquire(unit->mutex);
\r
774 xbt_dynar_foreach(unit->runner->variables, i, variable)
\r
776 if(!strcmp(variable->name, name))
\r
778 env = variable->env;
\r
779 err = variable->err;
\r
788 /*xbt_dynar_remove_at(unit->runner->variables, i, NULL);*/
\r
789 xbt_dynar_cursor_rm(unit->runner->variables, &i);
\r
791 WARN3("variable %s not found %s %s", name, line, filepos);
\r
794 WARN3("%s is an environment variable use unsetenv metacommand to delete it %s %s", name, line, filepos);
\r
796 WARN3("%s is an error variable : you are not allowed to delete it %s %s", name, line, filepos);
\r
799 xbt_os_mutex_release(unit->mutex);
\r
805 char name[PATH_MAX + 1] = {0};
\r
807 val = strchr(line, '=');
\r
811 variable_t variable;
\r
820 ERROR2("Indefinite variable value %s %s", line, filepos);
\r
821 syntax_error(unit);
\r
825 /* assume it's a varibale */
\r
826 strncpy(name, line, (val - line -1));
\r
828 xbt_os_mutex_acquire(unit->mutex);
\r
830 /* test if the variable is already registred */
\r
831 xbt_dynar_foreach(unit->runner->variables, i, variable)
\r
833 if(!strcmp(variable->name, name))
\r
840 /* if the variable is already registred, update its value;
\r
841 * otherwise register it.
\r
845 free(variable->val);
\r
846 variable->val = strdup(val);
\r
850 variable_t new_var = variable_new(name, val);
\r
851 xbt_dynar_push(unit->runner->variables, &new_var);
\r
855 xbt_os_mutex_release(unit->mutex);
\r
860 ERROR2("%s: Malformed metacommand: %s",filepos,line);
\r
861 syntax_error(unit);
\r
872 fstream_handle_include(fstream_t fstream, context_t context, xbt_os_mutex_t mutex, const char* file_name, const char* description)
\r
875 char* prev_directory = NULL;
\r
876 fstream_t _fstream = NULL;
\r
877 struct stat buffer = {0};
\r
878 unit_t unit = fstream->unit;
\r
880 if(!stat(file_name, &buffer) && S_ISREG(buffer.st_mode))
\r
882 /* the file is in the current directory */
\r
883 _fstream = fstream_new(getcwd(NULL, 0), file_name);
\r
884 fstream_open(_fstream);
\r
886 /* the file to include is not in the current directory, check if it is in a include directory */
\r
890 prev_directory = getcwd(NULL, 0);
\r
892 xbt_dynar_foreach(include_dirs, i, dir)
\r
896 if(!stat(file_name, &buffer) && S_ISREG(buffer.st_mode))
\r
898 _fstream = fstream_new(dir->name, file_name);
\r
899 fstream_open(_fstream);
\r
904 chdir(prev_directory);
\r
905 free(prev_directory);
\r
910 /* the file to include is not found handle the failure */
\r
913 exit_code = EINCLUDENOTFOUND;
\r
914 ERROR1("Include file %s not found",file_name);
\r
920 if(!unit->is_running_suite)
\r
921 {/* it's the unit of a suite */
\r
922 unit_t include = unit_new(unit->runner,unit->root, unit, _fstream);
\r
924 include->mutex = unit->root->mutex;
\r
927 include->description = strdup(description);
\r
929 xbt_dynar_push(unit->includes, &include);
\r
931 fstream_parse(_fstream, mutex);
\r
934 {/* it's a include */
\r
939 owner = xbt_dynar_get_ptr(unit->suites, xbt_dynar_length(unit->suites) - 1);
\r
941 include = unit_new(unit->runner,unit->root, *owner, _fstream);
\r
943 include->mutex = unit->root->mutex;
\r
946 include->description = strdup(description);
\r
948 xbt_dynar_push((*owner)->includes, &include);
\r
949 fstream_parse(_fstream, mutex);
\r
956 fstream_handle_suite(fstream_t fstream, context_t context, xbt_os_mutex_t mutex, const char* description)
\r
958 unit_t unit = fstream->unit;
\r
959 unit_t suite = unit_new(unit->runner, unit->root, unit, NULL);
\r
961 suite->description = strdup(description);
\r
962 xbt_dynar_push(unit->suites, &suite);
\r
963 unit->is_running_suite = 1;
\r
969 fstream_launch_command(fstream_t fstream, context_t context, xbt_os_mutex_t mutex)
\r
971 unit_t unit = fstream->unit;
\r
973 /* TODO : check the parameters */
\r
979 if(!(command = command_new(unit, context, mutex)))
\r
981 if(EINVAL == errno)
\r
983 ERROR2("Internal error : command_new() failed with the error %s (%d)",strerror(errno), errno);
\r
984 internal_error(unit);
\r
987 else if(ENOMEM == errno)
\r
989 ERROR1("System error : command_new() failed with the error code %d",errno);
\r
995 if(command_run(command) < 0)
\r
997 ERROR2("Internal error : command_run() failed with the error %s (%d)",strerror(errno), errno);
\r
998 internal_error(unit);
\r
1003 if(context_reset(context) < 0)
\r
1005 ERROR2("Internal error : command_run() failed with the error %s (%d)",strerror(errno), errno);
\r
1006 internal_error(unit);
\r