@@ -348,6 +348,155 @@ def pofilter(po: "CFunctionPO") -> bool:
348348 exit (0 )
349349
350350
351+ def cfile_run_file (args : argparse .Namespace ) -> NoReturn :
352+ """Parses, analyzes, and outputs a report for a single file."""
353+
354+ # arguments
355+ pcfilename : str = os .path .abspath (args .filename )
356+ opttgtpath : Optional [str ] = args .tgtpath
357+ cshowcode : bool = args .showcode
358+ copen : bool = args .open
359+ jsonoutput : bool = args .json
360+ outputfile : Optional [str ] = args .output
361+ loglevel : str = args .loglevel
362+ logfilename : Optional [str ] = args .logfilename
363+ logfilemode : str = args .logfilemode
364+
365+ try :
366+ UF .check_parser ()
367+ except UF .CHError as e :
368+ print (str (e .wrap ()))
369+ exit (1 )
370+
371+ if not os .path .isfile (os .path .abspath (pcfilename )):
372+ print_error ("C source code file " + pcfilename + " not found" )
373+ exit (1 )
374+
375+ if not pcfilename .endswith (".c" ):
376+ print_error ("C source code file should have extension .c" )
377+ exit (1 )
378+
379+ projectpath = os .path .dirname (os .path .abspath (pcfilename ))
380+ targetpath = projectpath if opttgtpath is None else opttgtpath
381+ targetpath = os .path .abspath (targetpath )
382+ cfilename_c = os .path .basename (pcfilename )
383+ cfilename = cfilename_c [:- 2 ]
384+ projectname = cfilename
385+
386+ if not os .path .isdir (targetpath ):
387+ print_error ("Target directory: " + targetpath + " does not exist" )
388+ exit (1 )
389+
390+ set_logging (
391+ loglevel ,
392+ targetpath ,
393+ logfilename = logfilename ,
394+ mode = logfilemode ,
395+ msg = "Command cfile run was invoked" )
396+
397+ chklogger .logger .info ("Target path: %s" , targetpath )
398+
399+ parsemanager = ParseManager (projectpath , projectname , targetpath )
400+ parsemanager .remove_semantics ()
401+ parsemanager .initialize_paths ()
402+
403+ try :
404+ cfilename_i = parsemanager .preprocess_file_with_gcc (cfilename_c )
405+ result = parsemanager .parse_ifile (cfilename_i )
406+ if result != 0 :
407+ print ("*" * 80 )
408+ print ("Error in parsing " + cfilename_c )
409+ if Config ().platform == "macOS" :
410+ print (" (Problem may be related to standard header files on macOS)" )
411+ print ("*" * 80 )
412+ exit (1 )
413+ except OSError as e :
414+ print_error ("Error when parsing file: " + str (e ))
415+ exit (1 )
416+
417+ parsemanager .save_semantics ()
418+
419+ chklogger .logger .info ("cfile parse completed" )
420+
421+ parsearchive = UF .get_parse_archive (targetpath , projectname )
422+
423+ if not os .path .isfile (parsearchive ):
424+ print_error ("Please run parser first on c file" )
425+ exit (1 )
426+
427+ cchpath = UF .get_cchpath (targetpath , projectname )
428+
429+ if os .path .isdir (cchpath ):
430+ chklogger .logger .info ("Old analysis results: %s are removed" , cchpath )
431+ shutil .rmtree (cchpath )
432+
433+ if os .path .isfile (parsearchive ):
434+ chklogger .logger .info ("Directory is changed to %s" , targetpath )
435+ os .chdir (targetpath )
436+ tarname = os .path .basename (parsearchive )
437+ cmd = ["tar" , "xfz" , os .path .basename (tarname )]
438+ chklogger .logger .info ("Semantics is extracted from %s" , tarname )
439+ result = subprocess .call (cmd , cwd = targetpath , stderr = subprocess .STDOUT )
440+ if result != 0 :
441+ print_error ("Error in extracting " + tarname )
442+ exit (1 )
443+ chklogger .logger .info (
444+ "Semantics was successfully extracted from %s" , tarname )
445+
446+ contractpath = os .path .join (targetpath , "chc_contracts" )
447+
448+ capp = CApplication (
449+ projectpath , projectname , targetpath , contractpath , singlefile = True )
450+
451+ capp .initialize_single_file (cfilename )
452+ cfile = capp .get_cfile ()
453+
454+ am = AnalysisManager (capp )
455+
456+ am .create_file_primary_proofobligations (cfilename )
457+ am .reset_tables (cfile )
458+ capp .collect_post_assumes ()
459+
460+ am .generate_and_check_file (cfilename , None , "llrvisp" )
461+ am .reset_tables (cfile )
462+ capp .collect_post_assumes ()
463+
464+ for k in range (5 ):
465+ capp .update_spos ()
466+ am .generate_and_check_file (cfilename , None , "llrvisp" )
467+ am .reset_tables (cfile )
468+
469+ chklogger .logger .info ("cfile analyze completed" )
470+
471+ capp = CApplication (
472+ projectpath , projectname , targetpath , contractpath , singlefile = True )
473+ capp .initialize_single_file (cfilename )
474+ cfile = capp .get_cfile ()
475+
476+ if jsonoutput :
477+ jsonresult = JU .file_proofobligations_to_json_result (cfile )
478+ if jsonresult .is_ok :
479+ jsonokresult = JU .jsonok ("fileproofobligations" , jsonresult .content )
480+ if outputfile :
481+ with open (outputfile + ".json" , "w" ) as fp :
482+ json .dump (jsonokresult , fp , indent = 2 )
483+ else :
484+ print (json .dumps (jsonokresult , indent = 2 ))
485+ exit (0 )
486+
487+ def pofilter (po : "CFunctionPO" ) -> bool :
488+ if copen :
489+ return not po .is_closed
490+ else :
491+ return True
492+
493+ if cshowcode :
494+ print (RP .file_code_tostring (cfile , pofilter = pofilter ))
495+
496+ print (RP .file_proofobligation_stats_tostring (cfile ))
497+ exit (0 )
498+
499+
351500def cfile_investigate_file (args : argparse .Namespace ) -> NoReturn :
352501 """Shows a list of open, delegated, and violated proof obligations.
353502
0 commit comments