Refactoring robotfindskitten

This section details the refactoring script used to transform the initial Rust translation of robotfindskitten, as generated by c2rust transpile, into a safe Rust program. We divide the refactoring process into several major steps:

  • ncurses macro cleanup: The ncurses library implements parts of its API using C preprocessor macros, and a few of those macros expand to relatively complex code. We replace these expanded macro bodies with calls to equivalent functions, which are easier to recognize and refactor.

  • String formatting: robotfindskitten calls several printf-style string-formatting functions. We replace these unsafe variable-argument function calls with safe wrappers using Rust's format family of macros. Aside from improving memory safety, this also allows the Rust compiler to more accurately typecheck the format arguments, which is helpful for later type-directed refactoring passes.

  • Static string constants: robotfindskitten has two global variables containing string constants, which are translated to Rust as static mut definitions containing C-style *const c_char pointers. We refactor to remove both sources of unsafety, replacing raw pointers with checked &'static str references and converting the mutable statics to immutable ones.

  • Heap allocations: robotfindskitten uses a heap allocated array to track the objects in the game world. This array is represented as a raw pointer, and the underlying storage is managed explicitly with malloc and free. We replace the array with a memory-safe collection type, avoiding unsafe FFI calls and preventing out-of-bounds memory accesses.

  • Using the pancurses library: Calling ncurses library functions directly through the Rust FFI requires unsafe code at every call site. We replace unsafe ncurses function calls with calls to the safe wrappers provided by the pancurses crate.

  • Moving global state to the stack: robotfindskitten uses mutable global variables to store the game state, which turn into unsafe static mut definitions in Rust. We collect all such variables into a single stack-allocated struct, which can be mutated without unsafety.

  • libc calls: We replace calls to miscellaneous libc functions, such as sleep and rand, with calls to safe Rust equivalents.

  • Function argument types: Two remaining functions in robotfindskitten take raw pointers as arguments. We change each function's signature to use only safe Rust types, and update their callers to match.

  • String conversion cleanup: Several of the previous refactoring passes insert conversions between Rust and C string types. In several places, these conversions form cycles, such as &str -> *const c_char -> &str, which are both redundant and a source of unsafe code. We remove such conversion cycles to avoid unnecessary raw pointer manipulation.

  • Final cleanup: At this point, we have removed all the unsafe code we can. Only a few cleanup steps remain, such as removing unused unsafe qualifiers and deleting unused extern "C" definitions. In the end, we are left with a correct Rust translation of robotfindskitten that contains only a single line of unsafe code.

ncurses macro cleanup

robotfindskitten uses a variety of macros provided by the ncurses library. Since c2rust transpile runs the C preprocessor before translating to Rust, the expansions of those macros effectively get inlined in the Rust code at each call site. In many cases, this is harmless: for example, move(y, x) expands to wmove(stdscr, y, x), which is not much harder to refactor than the original. However, the attr_get and attrset macros are more complex: they expand to multiple lines of code involving several conditionals and complex expressions. In this step, we convert the expanded code into simple function calls, which are easier to manipulate in later refactoring passes.

Fortunately, the ncurses library provides functions implementing the same operations as the troublesome macros, and we can call those functions through Rust's FFI. We begin by providing Rust declarations for these foreign functions. For ease of reading, we put the new declarations just after the existing extern "C" block:

select target 'crate; child(foreign_mod); last;' ;
create_item
    '
        extern "C" {
            fn wattr_get(win: *mut WINDOW, attrs: *mut attr_t,
                pair: *mut libc::c_short, opts: *mut libc::c_void) -> libc::c_int;
            fn wattrset(win: *mut WINDOW, attrs: libc::c_int) -> libc::c_int;
        }
    '
    after ;

Diff #1

src/robotfindskitten.rs
8
)]
8
)]
9
#![feature(const_raw_ptr_to_usize_cast, extern_types, libc)]
9
#![feature(const_raw_ptr_to_usize_cast, extern_types, libc)]
10
extern crate libc;
10
extern crate libc;
11
extern "C" {
11
extern "C" {
12
    pub type ldat;
12
    pub type ldat;
13
    #[no_mangle]
13
    #[no_mangle]
14
    fn printf(_: *const libc::c_char, ...) -> libc::c_int;
14
    fn printf(_: *const libc::c_char, ...) -> libc::c_int;
15
    #[no_mangle]
15
    #[no_mangle]
16
    fn cbreak() -> libc::c_int;
16
    fn cbreak() -> libc::c_int;

77
    #[no_mangle]
77
   #[no_mangle]
78
    fn time(__timer: *mut time_t) -> time_t;
78
    fn time(__timer: *mut time_t) -> time_t;
79
    #[no_mangle]
79
    #[no_mangle]
80
    fn sleep(__seconds: libc::c_uint) -> libc::c_uint;
80
    fn sleep(__seconds: libc::c_uint) -> libc::c_uint;
81
}
81
}
82
extern "C" {
83
    fn wattr_get(
84
        win: *mut WINDOW,
85
        attrs: *mut attr_t,
86
        pair: *mut libc::c_short,
87
        opts: *mut libc::c_void,
88
    ) -> libc::c_int;
89
    fn wattrset(win: *mut WINDOW, attrs: libc::c_int) -> libc::c_int;
90
}
82
pub type __time_t = libc::c_long;
91
pub type __time_t = libc::c_long;
83
pub type chtype = libc::c_ulong;
92
pub type chtype = libc::c_ulong;
93
94
#[repr(C)]
84
#[derive(Copy, Clone)]
95
#[derive(Copy, Clone)]
85
#[repr(C)]
86
pub struct _win_st {
96
pub struct _win_st {
87
    pub _cury: libc::c_short,
97
    pub _cury: libc::c_short,
88
    pub _curx: libc::c_short,
98
    pub _curx: libc::c_short,
89
    pub _maxy: libc::c_short,
99
    pub _maxy: libc::c_short,
90
    pub _maxx: libc::c_short,
100
    pub _maxx: libc::c_short,

110
    pub _pary: libc::c_int,
120
    pub _pary: libc::c_int,
111
    pub _parent: *mut WINDOW,
121
    pub _parent: *mut WINDOW,
112
    pub _pad: pdat,
122
    pub _pad: pdat,
113
    pub _yoffset: libc::c_short,
123
    pub _yoffset: libc::c_short,
114
}
124
}
125
126
#[repr(C)]
115
#[derive(Copy, Clone)]
127
#[derive(Copy, Clone)]
116
#[repr(C)]
117
pub struct pdat {
128
pub struct pdat {
118
    pub _pad_y: libc::c_short,
129
    pub _pad_y: libc::c_short,
119
    pub _pad_x: libc::c_short,
130
    pub _pad_x: libc::c_short,
120
    pub _pad_top: libc::c_short,
131
    pub _pad_top: libc::c_short,
121
    pub _pad_left: libc::c_short,
132
    pub _pad_left: libc::c_short,

156
/*Screen dimensions.*/
167
/*Screen dimensions.*/
157
/*Macros for generating numbers in different ranges*/
168
/*Macros for generating numbers in different ranges*/
158
/*Row constants for the animation*/
169
/*Row constants for the animation*/
159
/*This struct contains all the information we need to display an object
170
/*This struct contains all the information we need to display an object
160
on the screen*/
171
on the screen*/
172
173
#[repr(C)]
161
#[derive(Copy, Clone)]
174
#[derive(Copy, Clone)]
162
#[repr(C)]
163
pub struct screen_object {
175
pub struct screen_object {
164
    pub x: libc::c_int,
176
    pub x: libc::c_int,
165
    pub y: libc::c_int,
177
    pub y: libc::c_int,
166
    pub color: libc::c_int,
178
    pub color: libc::c_int,
167
    pub bold: bool,
179
    pub bold: bool,

Now we can use rewrite_expr to find Rust code that comes from the expansions of the wattrset macro and replace it with calls to the wattrset function:

rewrite_expr
    '
        if !(__win as *const libc::c_void).is_null() {
            (*__win)._attrs = __attrs
        } else {
        }
    '
    'wattrset(__win, __attrs as libc::c_int)' ;

Diff #2

src/robotfindskitten.rs
1202
        new |= 1u64 << 14i32 + 8i32
1202
        new |= 1u64 << 14i32 + 8i32
1203
    }
1203
    }
1204
    if o.bold {
1204
    if o.bold {
1205
        new |= 1u64 << 13i32 + 8i32
1205
        new |= 1u64 << 13i32 + 8i32
1206
    }
1206
    }
1207
    if !(stdscr as *const libc::c_void).is_null() {
1207
    wattrset(stdscr, new as libc::c_int);
1208
        (*stdscr)._attrs = new
1209
    } else {
1210
    };
1211
    if in_place {
1208
    if in_place {
1212
        printw(
1209
        printw(
1213
            b"%c\x00" as *const u8 as *const libc::c_char,
1210
            b"%c\x00" as *const u8 as *const libc::c_char,
1214
            o.character as libc::c_int,
1211
            o.character as libc::c_int,
1215
        );
1212
        );

1220
            b"%c\x00" as *const u8 as *const libc::c_char,
1217
            b"%c\x00" as *const u8 as *const libc::c_char,
1221
            o.character as libc::c_int,
1218
            o.character as libc::c_int,
1222
        );
1219
        );
1223
        wmove(stdscr, o.y, o.x);
1220
        wmove(stdscr, o.y, o.x);
1224
    }
1221
    }
1225
    if !(stdscr as *const libc::c_void).is_null() {
1222
    wattrset(stdscr, old as libc::c_int);
1226
        (*stdscr)._attrs = old
1227
    } else {
1228
    };
1229
}
1223
}
1230
#[no_mangle]
1224
#[no_mangle]
1231
pub unsafe extern "C" fn instructions() {
1225
pub unsafe extern "C" fn instructions() {
1232
    let mut dummy: libc::c_char = 0;
1226
    let mut dummy: libc::c_char = 0;
1233
    mvprintw(
1227
    mvprintw(

The __win and __attrs metavariables in the pattern correspond to the arguments of the original C macro, and are used in the replacement to construct the equivalent Rust function call.

Next, we do the same thing for the more complicated wattr_get macro:

rewrite_expr
    '
        if !(__win as *const libc::c_void).is_null() {
            if !(&mut __attrs as *mut attr_t as *const libc::c_void).is_null() {
                __attrs = (*__win)._attrs
            } else {
            };
            if !(&mut __pair as *mut libc::c_short as *const libc::c_void).is_null() {
                __pair = (((*__win)._attrs as libc::c_ulong
                    & ((1u32 << 8i32).wrapping_sub(1u32) << 0i32 + 8i32) as libc::c_ulong)
                    >> 8i32) as libc::c_int as libc::c_short
            } else {
            };
        } else {
        }
    '
    'wattr_get(__win, &mut __attrs, &mut __pair, ::std::ptr::null_mut())' ;

Finally, we are done with this bit of cleanup, so we write the changes to disk before continuing on:

commit ;

Diff #4

src/robotfindskitten.rs
8
)]
8
)]
9
#![feature(const_raw_ptr_to_usize_cast, extern_types, libc)]
9
#![feature(const_raw_ptr_to_usize_cast, extern_types, libc)]
10
extern crate libc;
10
extern crate libc;
11
extern "C" {
11
extern "C" {
12
    pub type ldat;
12
    pub type ldat;
13
    #[no_mangle]
13
    #[no_mangle]
14
    fn printf(_: *const libc::c_char, ...) -> libc::c_int;
14
    fn printf(_: *const libc::c_char, ...) -> libc::c_int;
15
    #[no_mangle]
15
    #[no_mangle]
16
    fn cbreak() -> libc::c_int;
16
    fn cbreak() -> libc::c_int;

79
   #[no_mangle]
79
    #[no_mangle]
80
    fn sleep(__seconds: libc::c_uint) -> libc::c_uint;
80
    fn sleep(__seconds: libc::c_uint) -> libc::c_uint;
81
}
81
}
82
extern "C" {
82
extern "C" {
83
    fn wattr_get(
83
    fn wattr_get(
84
        win: *mut WINDOW,
84
        win: *mut WINDOW,
85
        attrs: *mut attr_t,
85
        attrs: *mut attr_t,
86
        pair: *mut libc::c_short,
86
        pair: *mut libc::c_short,
87
        opts: *mut libc::c_void,
87
        opts: *mut libc::c_void,

String formatting

robotfindskitten calls several printf-style variable-argument functions to perform string formatting. Since variable-argument function calls are considered unsafe in Rust, we must replace these with Rust-style string formatting using format! and related macros. Specifically, for each string formatting function such as printf, we will create a safe wrapper fmt_printf that takes a Rust fmt::Arguments object, and replace printf(...) calls with fmt_printf(format_args!(...)). This approach isolates all the unsafety into the fmt_printf wrapper, where it can be eliminated by later passes.

The replacement itself happens in two steps. First, we convert printf calls from printf(<C format args...>) to printf(format_args!(<Rust format args...>)). Note that the code does not typecheck in this intermediate state: C's printf function cannot accept the std::fmt::Arguments produced by the format_args! macro. The second step then replaces the printf call with a call to the fmt_printf wrapper, which does accept std::fmt::Arguments.

printf format argument conversion

We run a few commands to mark the nodes involved in string formatting, before finally running the convert_format_args command to perform the actual transformation.

First, we use select and mark_arg_uses to mark the first argument of every printf call as targets:

select target 'item(printf);' ;
mark_arg_uses 0 target ;

Diff #5

src/robotfindskitten.rs
977
unsafe extern "C" fn finish(mut sig: libc::c_int) {
977
unsafe extern "C" fn finish(mut sig: libc::c_int) {
978
    endwin();
978
    endwin();
979
    printf(
979
    printf(
980
        b"%c%c%c\x00" as *const u8 as *const libc::c_char,
980
        b"%c%c%c\x00" as *const u8 as *const libc::c_char,
981
        27i32,
981
        27i32,
982
        '(' as i32,
982
        '(' as i32,
983
        'B' as i32,
983
        'B' as i32,
984
    );
984
    );
985
    exit(0i32);
985
    exit(0i32);

1419
                    .wrapping_div(::std::mem::size_of::<*mut libc::c_char>() as libc::c_ulong)
1419
                    .wrapping_div(::std::mem::size_of::<*mut libc::c_char>() as libc::c_ulong)
1420
        {
1420
        {
1421
            printf(
1421
            printf(
1422
                b"Run-time parameter must be between 0 and %d.\n\x00" as *const u8
1422
                b"Run-time parameter must be between 0 and %d.\n\x00" as *const u8
1423
                    as *const libc::c_char,
1423
                    as *const libc::c_char,
1424
                (::std::mem::size_of::<[*mut libc::c_char; 406]>() as libc::c_ulong)
1424
                (::std::mem::size_of::<[*mut libc::c_char; 406]>() as libc::c_ulong)
1425
                    .wrapping_div(::std::mem::size_of::<*mut libc::c_char>() as libc::c_ulong),
1425
                    .wrapping_div(::std::mem::size_of::<*mut libc::c_char>() as libc::c_ulong),
1426
            );
1426
            );
1427
            exit(0i32);
1427
            exit(0i32);

1429
    }
1429
    }
1430
    srand(time(0 as *mut time_t) as libc::c_uint);
1430
    srand(time(0 as *mut time_t) as libc::c_uint);
1431
    printf(
1431
    printf(
1432
        b"%c%c%c\x00" as *const u8 as *const libc::c_char,
1432
        b"%c%c%c\x00" as *const u8 as *const libc::c_char,
1433
        27i32,
1433
        27i32,
1434
        '(' as i32,
1434
        '(' as i32,
1435
        'U' as i32,
1435
        'U' as i32,
1436
    );
1436
    );
1437
    initialize_ncurses();
1437
    initialize_ncurses();

convert_format_args will treat the target argument at each call site as a printf-style format string, and will treat all later arguments as format args.

Next, we mark the format string literal with fmt_str, which tells convert_format_args the exact string literal it should use as the format string. This usually is not the same as the target argument, since c2rust-transpile inserts several casts to turn a Rust string literal into a *const libc::c_char.

select fmt_str 'marked(target); desc(expr && !match_expr(__e as __t));' ;

Diff #6

src/robotfindskitten.rs
977
unsafe extern "C" fn finish(mut sig: libc::c_int) {
977
unsafe extern "C" fn finish(mut sig: libc::c_int) {
978
    endwin();
978
    endwin();
979
    printf(
979
    printf(
980
        b"%c%c%c\x00" as *const u8 as *const libc::c_char,
980
        b"%c%c%c\x00" as *const u8 as *const libc::c_char,
981
        27i32,
981
        27i32,
982
        '(' as i32,
982
        '(' as i32,
983
        'B' as i32,
983
        'B' as i32,
984
    );
984
    );
985
    exit(0i32);
985
    exit(0i32);

1419
                    .wrapping_div(::std::mem::size_of::<*mut libc::c_char>() as libc::c_ulong)
1419
                    .wrapping_div(::std::mem::size_of::<*mut libc::c_char>() as libc::c_ulong)
1420
        {
1420
        {
1421
            printf(
1421
            printf(
1422
                b"Run-time parameter must be between 0 and %d.\n\x00" as *const u8
1422
                b"Run-time parameter must be between 0 and %d.\n\x00" as *const u8
1423
                    as *const libc::c_char,
1423
                    as *const libc::c_char,
1424
                (::std::mem::size_of::<[*mut libc::c_char; 406]>() as libc::c_ulong)
1424
                (::std::mem::size_of::<[*mut libc::c_char; 406]>() as libc::c_ulong)
1425
                    .wrapping_div(::std::mem::size_of::<*mut libc::c_char>() as libc::c_ulong),
1425
                    .wrapping_div(::std::mem::size_of::<*mut libc::c_char>() as libc::c_ulong),
1426
            );
1426
            );
1427
            exit(0i32);
1427
            exit(0i32);

1429
    }
1429
    }
1430
    srand(time(0 as *mut time_t) as libc::c_uint);
1430
    srand(time(0 as *mut time_t) as libc::c_uint);
1431
    printf(
1431
    printf(
1432
        b"%c%c%c\x00" as *const u8 as *const libc::c_char,
1432
        b"%c%c%c\x00" as *const u8 as *const libc::c_char,
1433
        27i32,
1433
        27i32,
1434
        '(' as i32,
1434
        '(' as i32,
1435
        'U' as i32,
1435
        'U' as i32,
1436
    );
1436
    );
1437
    initialize_ncurses();
1437
    initialize_ncurses();

With both target and fmt_str marks in place, we can apply the actual transformation:

convert_format_args ;

Diff #7

src/robotfindskitten.rs
974
        );
974
        );
975
    };
975
    };
976
}
976
}
977
unsafe extern "C" fn finish(mut sig: libc::c_int) {
977
unsafe extern "C" fn finish(mut sig: libc::c_int) {
978
    endwin();
978
    endwin();
979
    printf(
979
    printf(format_args!(
980
        b"%c%c%c\x00" as *const u8 as *const libc::c_char,
980
        "{:}{:}{:}",
981
        27i32,
981
        27i32 as u8 as char, '(' as i32 as u8 as char, 'B' as i32 as u8 as char
982
        '(' as i32,
983
        'B' as i32,
984
    );
982
    ));
985
    exit(0i32);
983
    exit(0i32);
986
}
984
}
987
#[no_mangle]
985
#[no_mangle]
988
pub unsafe extern "C" fn initialize_arrays() {
986
pub unsafe extern "C" fn initialize_arrays() {
989
    let mut counter: libc::c_int = 0;
987
    let mut counter: libc::c_int = 0;

1416
        if num_bogus < 0i32
1414
        if num_bogus < 0i32
1417
            || num_bogus as libc::c_ulong
1415
            || num_bogus as libc::c_ulong
1418
                > (::std::mem::size_of::<[*mut libc::c_char; 406]>() as libc::c_ulong)
1416
                > (::std::mem::size_of::<[*mut libc::c_char; 406]>() as libc::c_ulong)
1419
                    .wrapping_div(::std::mem::size_of::<*mut libc::c_char>() as libc::c_ulong)
1417
                    .wrapping_div(::std::mem::size_of::<*mut libc::c_char>() as libc::c_ulong)
1420
        {
1418
        {
1421
            printf(
1419
            printf(format_args!(
1422
                b"Run-time parameter must be between 0 and %d.\n\x00" as *const u8
1420
                "Run-time parameter must be between 0 and {:}.\n",
1423
                    as *const libc::c_char,
1424
                (::std::mem::size_of::<[*mut libc::c_char; 406]>() as libc::c_ulong)
1421
                (::std::mem::size_of::<[*mut libc::c_char; 406]>() as libc::c_ulong)
1425
                    .wrapping_div(::std::mem::size_of::<*mut libc::c_char>() as libc::c_ulong),
1422
                    .wrapping_div(::std::mem::size_of::<*mut libc::c_char>() as libc::c_ulong)
1423
                    as libc::c_int
1426
            );
1424
            ));
1427
            exit(0i32);
1425
            exit(0i32);
1428
        }
1426
        }
1429
    }
1427
    }
1430
    srand(time(0 as *mut time_t) as libc::c_uint);
1428
    srand(time(0 as *mut time_t) as libc::c_uint);
1431
    printf(
1429
    printf(format_args!(
1432
        b"%c%c%c\x00" as *const u8 as *const libc::c_char,
1430
        "{:}{:}{:}",
1433
        27i32,
1431
        27i32 as u8 as char, '(' as i32 as u8 as char, 'U' as i32 as u8 as char
1434
        '(' as i32,
1435
        'U' as i32,
1436
    );
1432
    ));
1437
    initialize_ncurses();
1433
    initialize_ncurses();
1438
    initialize_arrays();
1434
    initialize_arrays();
1439
    initialize_robot();
1435
    initialize_robot();
1440
    initialize_kitten();
1436
    initialize_kitten();
1441
    initialize_bogus();
1437
    initialize_bogus();

Finally, we clean up from this step by clearing all the marks.

clear_marks ;

commit would also clear the marks, but we don't want to commit these changes until we've fixed the type errors introduced in this step.

Creating a printf wrapper

As a reminder, we currently have code that looks like this:

printf(format_args!("Hello, {}!\n", "world"))

printf itself can't accept the std::fmt::Arguments returned by format_args!, so we will define a wrapper that does accept std::fmt::Arguments and then rewrite these printf calls to call the wrapper instead.

First, we insert the wrapper:

select target 'crate; child(foreign_mod); last;' ;
create_item
    '
        fn fmt_printf(args: ::std::fmt::Arguments) -> libc::c_int {
            print!("{}", args);
            0
        }
    '
    after ;

Diff #9

src/robotfindskitten.rs
79
    #[no_mangle]
79
    #[no_mangle]
80
    fn sleep(__seconds: libc::c_uint) -> libc::c_uint;
80
    fn sleep(__seconds: libc::c_uint) -> libc::c_uint;
81
}
81
}
82
extern "C" {
82
extern "C" {
83
    fn wattr_get(
83
    fn wattr_get(
84
        win: *mut WINDOW,
84
        win: *mut WINDOW,
85
        attrs: *mut attr_t,
85
        attrs: *mut attr_t,
86
        pair: *mut libc::c_short,
86
        pair: *mut libc::c_short,
87
        opts: *mut libc::c_void,
87
        opts: *mut libc::c_void,
88
    ) -> libc::c_int;
88
    ) -> libc::c_int;
89
    fn wattrset(win: *mut WINDOW, attrs: libc::c_int) -> libc::c_int;
89
    fn wattrset(win: *mut WINDOW, attrs: libc::c_int) -> libc::c_int;
90
}
90
}
91
fn fmt_printf(args: ::std::fmt::Arguments) -> libc::c_int {
92
    print!("{}", args);
93
    0
94
}
91
pub type __time_t = libc::c_long;
95
pub type __time_t = libc::c_long;
92
pub type chtype = libc::c_ulong;
96
pub type chtype = libc::c_ulong;
93
97
94
#[repr(C)]
98
#[repr(C)]
95
#[derive(Copy, Clone)]
99
#[derive(Copy, Clone)]

Since Rust provides a print! macro with similar functionality to printf, our "wrapper" actually just calls print! directly, avoiding the string conversions necessary to call the actual C printf. (See the next subsection for an example of a "real" wrapper function.)

With the wrapper in place, we can now update the call sites:

rewrite_expr 'printf' 'fmt_printf' ;

Diff #10

src/robotfindskitten.rs
978
        );
978
        );
979
    };
979
    };
980
}
980
}
981
unsafe extern "C" fn finish(mut sig: libc::c_int) {
981
unsafe extern "C" fn finish(mut sig: libc::c_int) {
982
    endwin();
982
    endwin();
983
    printf(format_args!(
983
    fmt_printf(format_args!(
984
        "{:}{:}{:}",
984
        "{:}{:}{:}",
985
        27i32 as u8 as char, '(' as i32 as u8 as char, 'B' as i32 as u8 as char
985
        27i32 as u8 as char, '(' as i32 as u8 as char, 'B' as i32 as u8 as char
986
    ));
986
    ));
987
    exit(0i32);
987
    exit(0i32);
988
}
988
}

1418
        if num_bogus < 0i32
1418
        if num_bogus < 0i32
1419
            || num_bogus as libc::c_ulong
1419
            || num_bogus as libc::c_ulong
1420
                > (::std::mem::size_of::<[*mut libc::c_char; 406]>() as libc::c_ulong)
1420
                > (::std::mem::size_of::<[*mut libc::c_char; 406]>() as libc::c_ulong)
1421
                    .wrapping_div(::std::mem::size_of::<*mut libc::c_char>() as libc::c_ulong)
1421
                    .wrapping_div(::std::mem::size_of::<*mut libc::c_char>() as libc::c_ulong)
1422
        {
1422
        {
1423
            printf(format_args!(
1423
            fmt_printf(format_args!(
1424
                "Run-time parameter must be between 0 and {:}.\n",
1424
                "Run-time parameter must be between 0 and {:}.\n",
1425
                (::std::mem::size_of::<[*mut libc::c_char; 406]>() as libc::c_ulong)
1425
                (::std::mem::size_of::<[*mut libc::c_char; 406]>() as libc::c_ulong)
1426
                    .wrapping_div(::std::mem::size_of::<*mut libc::c_char>() as libc::c_ulong)
1426
                    .wrapping_div(::std::mem::size_of::<*mut libc::c_char>() as libc::c_ulong)
1427
                    as libc::c_int
1427
                    as libc::c_int
1428
            ));
1428
            ));
1429
            exit(0i32);
1429
            exit(0i32);
1430
        }
1430
        }
1431
    }
1431
    }
1432
    srand(time(0 as *mut time_t) as libc::c_uint);
1432
    srand(time(0 as *mut time_t) as libc::c_uint);
1433
    printf(format_args!(
1433
    fmt_printf(format_args!(
1434
        "{:}{:}{:}",
1434
        "{:}{:}{:}",
1435
        27i32 as u8 as char, '(' as i32 as u8 as char, 'U' as i32 as u8 as char
1435
        27i32 as u8 as char, '(' as i32 as u8 as char, 'U' as i32 as u8 as char
1436
    ));
1436
    ));
1437
    initialize_ncurses();
1437
    initialize_ncurses();
1438
    initialize_arrays();
1438
    initialize_arrays();

Now that we've finished this step and the crate typechecks again, we can safely commit the changes:

commit ;

Diff #11

src/robotfindskitten.rs
79
    #[no_mangle]
79
    #[no_mangle]
80
    fn sleep(__seconds: libc::c_uint) -> libc::c_uint;
80
    fn sleep(__seconds: libc::c_uint) -> libc::c_uint;
81
}
81
}
82
extern "C" {
82
extern "C" {
83
    fn wattr_get(
83
    fn wattr_get(
84
        win: *mut WINDOW,
84
        win: *mut WINDOW,
85
        attrs: *mut attr_t,
85
        attrs: *mut attr_t,
86
        pair: *mut libc::c_short,
86
        pair: *mut libc::c_short,
87
        opts: *mut libc::c_void,
87
        opts: *mut libc::c_void,
88
    ) -> libc::c_int;
88
    ) -> libc::c_int;
89
    fn wattrset(win: *mut WINDOW, attrs: libc::c_int) -> libc::c_int;
89
    fn wattrset(win: *mut WINDOW, attrs: libc::c_int) -> libc::c_int;
90
}
90
}
91
fn fmt_printf(args: ::std::fmt::Arguments) -> libc::c_int {
91
fn fmt_printf(args: ::std::fmt::Arguments) -> libc::c_int {
92
    print!("{}", args);
92
    print!("{}", args);
93
    0
93
    0
94
}
94
}
95
pub type __time_t = libc::c_long;
95
pub type __time_t = libc::c_long;
96
pub type chtype = libc::c_ulong;
96
pub type chtype = libc::c_ulong;

Other string formatting functions

Aside from printf, robotfindskitten also uses the ncurses printw and mvprintw string-formatting functions. The refactoring script for printw is similar to the previous two steps combined:

select target 'item(printw);' ;
mark_arg_uses 0 target ;
select fmt_str 'marked(target); desc(expr && !match_expr(__e as __t));' ;

convert_format_args ;

clear_marks ;

select target 'crate; child(foreign_mod); last;' ;
create_item
    '
        fn fmt_printw(args: ::std::fmt::Arguments) -> libc::c_int {
            unsafe {
                ::printw(b"%s\0" as *const u8 as *const libc::c_char,
                         ::std::ffi::CString::new(format!("{}", args))
                             .unwrap().as_ptr())
            }
        }
    '
    after ;
rewrite_expr 'printw' 'fmt_printw' ;
commit ;

Diff #12

src/robotfindskitten.rs
86
        pair: *mut libc::c_short,
86
        pair: *mut libc::c_short,
87
        opts: *mut libc::c_void,
87
        opts: *mut libc::c_void,
88
    ) -> libc::c_int;
88
    ) -> libc::c_int;
89
    fn wattrset(win: *mut WINDOW, attrs: libc::c_int) -> libc::c_int;
89
    fn wattrset(win: *mut WINDOW, attrs: libc::c_int) -> libc::c_int;
90
}
90
}
91
fn fmt_printw(args: ::std::fmt::Arguments) -> libc::c_int {
92
    unsafe {
93
        ::printw(
94
            b"%s\0" as *const u8 as *const libc::c_char,
95
            ::std::ffi::CString::new(format!("{}", args))
96
                .unwrap()
97
                .as_ptr(),
98
        )
99
    }
100
}
91
fn fmt_printf(args: ::std::fmt::Arguments) -> libc::c_int {
101
fn fmt_printf(args: ::std::fmt::Arguments) -> libc::c_int {
92
    print!("{}", args);
102
    print!("{}", args);
93
    0
103
    0
94
}
104
}
95
pub type __time_t = libc::c_long;
105
pub type __time_t = libc::c_long;

1161
        b"robotfindskitten v%s\n\n\x00" as *const u8 as *const libc::c_char,
1171
        b"robotfindskitten v%s\n\n\x00" as *const u8 as *const libc::c_char,
1162
        ver,
1172
        ver,
1163
    );
1173
    );
1164
    counter = 0i32;
1174
    counter = 0i32;
1165
    while counter <= COLS - 1i32 {
1175
    while counter <= COLS - 1i32 {
1166
        printw(b"%c\x00" as *const u8 as *const libc::c_char, 95i32);
1176
        fmt_printw(format_args!("{:}", 95i32 as u8 as char));
1167
        counter += 1
1177
        counter += 1
1168
    }
1178
    }
1169
    counter = 0i32;
1179
    counter = 0i32;
1170
    while counter < num_bogus {
1180
    while counter < num_bogus {
1171
        draw(bogus[counter as usize]);
1181
        draw(bogus[counter as usize]);

1206
    if o.bold {
1216
    if o.bold {
1207
        new |= 1u64 << 13i32 + 8i32
1217
        new |= 1u64 << 13i32 + 8i32
1208
    }
1218
    }
1209
    wattrset(stdscr, new as libc::c_int);
1219
    wattrset(stdscr, new as libc::c_int);
1210
    if in_place {
1220
    if in_place {
1211
        printw(
1221
        fmt_printw(format_args!(
1212
            b"%c\x00" as *const u8 as *const libc::c_char,
1222
            "{:}",
1213
            o.character as libc::c_int,
1223
            o.character as libc::c_int as u8 as char
1214
        );
1224
        ));
1215
    } else {
1225
    } else {
1216
        mvprintw(
1226
        mvprintw(
1217
            o.y,
1227
            o.y,
1218
            o.x,
1228
            o.x,
1219
            b"%c\x00" as *const u8 as *const libc::c_char,
1229
            b"%c\x00" as *const u8 as *const libc::c_char,

1230
        0i32,
1240
        0i32,
1231
        0i32,
1241
        0i32,
1232
        b"robotfindskitten v%s\n\x00" as *const u8 as *const libc::c_char,
1242
        b"robotfindskitten v%s\n\x00" as *const u8 as *const libc::c_char,
1233
        ver,
1243
        ver,
1234
    );
1244
    );
1235
    printw(
1245
    fmt_printw(format_args!(
1236
        b"By the illustrious Leonard Richardson (C) 1997, 2000\n\x00" as *const u8
1246
        "By the illustrious Leonard Richardson (C) 1997, 2000\n"
1237
            as *const libc::c_char,
1238
    );
1247
    ));
1239
    printw(
1248
    fmt_printw(format_args!(
1240
        b"Written originally for the Nerth Pork robotfindskitten contest\n\n\x00" as *const u8
1249
        "Written originally for the Nerth Pork robotfindskitten contest\n\n"
1241
            as *const libc::c_char,
1242
    );
1250
    ));
1243
    printw(b"In this game, you are robot (\x00" as *const u8 as *const libc::c_char);
1251
    fmt_printw(format_args!("In this game, you are robot ("));
1244
    draw_in_place(robot);
1252
    draw_in_place(robot);
1245
    printw(b"). Your job is to find kitten. This task\n\x00" as *const u8 as *const libc::c_char);
1253
    fmt_printw(format_args!("). Your job is to find kitten. This task\n"));
1246
    printw(
1254
    fmt_printw(format_args!(
1247
        b"is complicated by the existence of various things which are not kitten.\n\x00"
1255
        "is complicated by the existence of various things which are not kitten.\n"
1248
            as *const u8 as *const libc::c_char,
1249
    );
1256
    ));
1250
    printw(
1257
    fmt_printw(format_args!(
1251
        b"Robot must touch items to determine if they are kitten or not. The game\n\x00"
1258
        "Robot must touch items to determine if they are kitten or not. The game\n"
1252
            as *const u8 as *const libc::c_char,
1253
    );
1259
    ));
1254
    printw(
1260
    fmt_printw(format_args!(
1255
        b"ends when robotfindskitten. Alternatively, you may end the game by hitting\n\x00"
1261
        "ends when robotfindskitten. Alternatively, you may end the game by hitting\n"
1256
            as *const u8 as *const libc::c_char,
1257
    );
1262
    ));
1258
    printw(
1263
    fmt_printw(format_args!(
1259
        b"the Esc key. See the documentation for more information.\n\n\x00" as *const u8
1264
        "the Esc key. See the documentation for more information.\n\n"
1260
            as *const libc::c_char,
1261
    );
1265
    ));
1262
    printw(b"Press any key to start.\n\x00" as *const u8 as *const libc::c_char);
1266
    fmt_printw(format_args!("Press any key to start.\n"));
1263
    wrefresh(stdscr);
1267
    wrefresh(stdscr);
1264
    dummy = wgetch(stdscr) as libc::c_char;
1268
    dummy = wgetch(stdscr) as libc::c_char;
1265
    wclear(stdscr);
1269
    wclear(stdscr);
1266
}
1270
}
1267
#[no_mangle]
1271
#[no_mangle]

Aside from replacing the name printf with printw, the other notable difference from the printf script is the body of fmt_printw. There is no convenient replacement for printw in the Rust standard library, so instead we call the original printw function, passing in the result of Rust string formatting (converted to a C string) as an argument.

The mvprintw replacement is also similar, just with a few extra arguments:

select target 'item(mvprintw);' ;
mark_arg_uses 2 target ;
select fmt_str 'marked(target); desc(expr && !match_expr(__e as __t));' ;

convert_format_args ;

clear_marks ;

select target 'crate; child(foreign_mod); last;' ;
create_item
    '
        fn fmt_mvprintw(y: libc::c_int, x: libc::c_int,
                        args: ::std::fmt::Arguments) -> libc::c_int {
            unsafe {
                ::mvprintw(y, x, b"%s\0" as *const u8 as *const libc::c_char,
                         ::std::ffi::CString::new(format!("{}", args))
                             .unwrap().as_ptr())
            }
        }
    '
    after ;
rewrite_expr 'mvprintw' 'fmt_mvprintw' ;
commit ;

Diff #13

src/robotfindskitten.rs
86
        pair: *mut libc::c_short,
86
        pair: *mut libc::c_short,
87
        opts: *mut libc::c_void,
87
        opts: *mut libc::c_void,
88
    ) -> libc::c_int;
88
    ) -> libc::c_int;
89
    fn wattrset(win: *mut WINDOW, attrs: libc::c_int) -> libc::c_int;
89
    fn wattrset(win: *mut WINDOW, attrs: libc::c_int) -> libc::c_int;
90
}
90
}
91
fn fmt_mvprintw(y: libc::c_int, x: libc::c_int, args: ::std::fmt::Arguments) -> libc::c_int {
92
    unsafe {
93
        ::mvprintw(
94
            y,
95
            x,
96
            b"%s\0" as *const u8 as *const libc::c_char,
97
            ::std::ffi::CString::new(format!("{}", args))
98
                .unwrap()
99
                .as_ptr(),
100
        )
101
    }
102
}
91
fn fmt_printw(args: ::std::fmt::Arguments) -> libc::c_int {
103
fn fmt_printw(args: ::std::fmt::Arguments) -> libc::c_int {
92
    unsafe {
104
    unsafe {
93
        ::printw(
105
        ::printw(
94
            b"%s\0" as *const u8 as *const libc::c_char,
106
            b"%s\0" as *const u8 as *const libc::c_char,
95
            ::std::ffi::CString::new(format!("{}", args))
107
            ::std::ffi::CString::new(format!("{}", args))

1163
#[no_mangle]
1175
#[no_mangle]
1164
pub static mut num_bogus: libc::c_int = 0;
1176
pub static mut num_bogus: libc::c_int = 0;
1165
#[no_mangle]
1177
#[no_mangle]
1166
pub unsafe extern "C" fn initialize_screen() {
1178
pub unsafe extern "C" fn initialize_screen() {
1167
    let mut counter: libc::c_int = 0;
1179
    let mut counter: libc::c_int = 0;
1168
    mvprintw(
1180
    fmt_mvprintw(
1169
        0i32,
1181
        0i32,
1170
        0i32,
1182
        0i32,
1171
        b"robotfindskitten v%s\n\n\x00" as *const u8 as *const libc::c_char,
1183
        format_args!("robotfindskitten v{:}\n\n", unsafe {
1184
            ::std::ffi::CStr::from_ptr(ver as *const libc::c_char)
1185
                .to_str()
1186
                .unwrap()
1172
        ver,
1187
        }),
1173
    );
1188
    );
1174
    counter = 0i32;
1189
    counter = 0i32;
1175
    while counter <= COLS - 1i32 {
1190
    while counter <= COLS - 1i32 {
1176
        fmt_printw(format_args!("{:}", 95i32 as u8 as char));
1191
        fmt_printw(format_args!("{:}", 95i32 as u8 as char));
1177
        counter += 1
1192
        counter += 1

1221
        fmt_printw(format_args!(
1236
        fmt_printw(format_args!(
1222
            "{:}",
1237
            "{:}",
1223
            o.character as libc::c_int as u8 as char
1238
            o.character as libc::c_int as u8 as char
1224
        ));
1239
        ));
1225
    } else {
1240
    } else {
1226
        mvprintw(
1241
        fmt_mvprintw(
1227
            o.y,
1242
            o.y,
1228
            o.x,
1243
            o.x,
1229
            b"%c\x00" as *const u8 as *const libc::c_char,
1244
            format_args!("{:}", o.character as libc::c_int as u8 as char),
1230
            o.character as libc::c_int,
1231
        );
1245
        );
1232
        wmove(stdscr, o.y, o.x);
1246
        wmove(stdscr, o.y, o.x);
1233
    }
1247
    }
1234
    wattrset(stdscr, old as libc::c_int);
1248
    wattrset(stdscr, old as libc::c_int);
1235
}
1249
}
1236
#[no_mangle]
1250
#[no_mangle]
1237
pub unsafe extern "C" fn instructions() {
1251
pub unsafe extern "C" fn instructions() {
1238
    let mut dummy: libc::c_char = 0;
1252
    let mut dummy: libc::c_char = 0;
1239
    mvprintw(
1253
    fmt_mvprintw(
1240
        0i32,
1254
        0i32,
1241
        0i32,
1255
        0i32,
1242
        b"robotfindskitten v%s\n\x00" as *const u8 as *const libc::c_char,
1256
        format_args!("robotfindskitten v{:}\n", unsafe {
1257
            ::std::ffi::CStr::from_ptr(ver as *const libc::c_char)
1258
                .to_str()
1259
                .unwrap()
1243
        ver,
1260
        }),
1244
    );
1261
    );
1245
    fmt_printw(format_args!(
1262
    fmt_printw(format_args!(
1246
        "By the illustrious Leonard Richardson (C) 1997, 2000\n"
1263
        "By the illustrious Leonard Richardson (C) 1997, 2000\n"
1247
    ));
1264
    ));
1248
    fmt_printw(format_args!(
1265
    fmt_printw(format_args!(

1301
}
1318
}
1302
#[no_mangle]
1319
#[no_mangle]
1303
pub unsafe extern "C" fn message(mut message_0: *mut libc::c_char) {
1320
pub unsafe extern "C" fn message(mut message_0: *mut libc::c_char) {
1304
    wmove(stdscr, 1i32, 0i32);
1321
    wmove(stdscr, 1i32, 0i32);
1305
    wclrtoeol(stdscr);
1322
    wclrtoeol(stdscr);
1306
    mvprintw(
1323
    fmt_mvprintw(
1307
        1i32,
1324
        1i32,
1308
        0i32,
1325
        0i32,
1309
        b"%.*s\x00" as *const u8 as *const libc::c_char,
1326
        format_args!("{:.*}", COLS as usize, unsafe {
1327
            ::std::ffi::CStr::from_ptr(message_0 as *const libc::c_char)
1328
                .to_str()
1329
                .unwrap()
1310
        COLS,
1330
        }),
1311
        message_0,
1312
    );
1331
    );
1313
    wmove(stdscr, robot.y, robot.x);
1332
    wmove(stdscr, robot.y, robot.x);
1314
    wrefresh(stdscr);
1333
    wrefresh(stdscr);
1315
}
1334
}
1316
#[no_mangle]
1335
#[no_mangle]

Static string constant - ver

robotfindskitten defines a static string constant, ver, to store the game's version. Using ver is currently unsafe, first because its Rust type is a raw pointer (*mut c_char), and second because it's mutable. To make ver usage safe, we first change its type to &'static str (and fix up the resulting type errors), and then we change it from a static mut to an ordinary immutable static. Note that we must change the type first because Rust does not allow raw pointers to be stored in safe (non-mut) statics.

We change the type using rewrite_ty:

select target 'item(ver); child(ty);' ;
rewrite_ty 'marked!(*mut libc::c_char)' "&'static str" ;
delete_marks target ;

Diff #14

src/robotfindskitten.rs
203
    pub y: libc::c_int,
203
    pub y: libc::c_int,
204
    pub color: libc::c_int,
204
    pub color: libc::c_int,
205
    pub bold: bool,
205
    pub bold: bool,
206
    pub character: libc::c_char,
206
    pub character: libc::c_char,
207
}
207
}
208
static mut ver: *mut libc::c_char =
208
static mut ver: &'static str =
209
    b"1.7320508.406\x00" as *const u8 as *const libc::c_char as *mut libc::c_char;
209
    b"1.7320508.406\x00" as *const u8 as *const libc::c_char as *mut libc::c_char;
210
#[inline]
210
#[inline]
211
unsafe extern "C" fn atoi(mut __nptr: *const libc::c_char) -> libc::c_int {
211
unsafe extern "C" fn atoi(mut __nptr: *const libc::c_char) -> libc::c_int {
212
    return strtol(
212
    return strtol(
213
        __nptr,
213
        __nptr,

The combination of select and the marked! matching form ensures that only ver's type annotation is modified. We delete the mark afterward, since it's no longer needed.

Simply replacing *mut c_char with &str introduces type errors throughout the crate. The initializer for ver still has type *mut c_char, and all uses of ver are still expecting a *mut c_char.

Fixing ver's initializer

Fixing the ver initializer is straightforward: we simply remove all the casts, then convert the binary string (&[u8]) literal to an ordinary string literal. For the casts, we mark all cast expressions in ver's definition, then replace each one with its subexpression:

select target 'item(ver); desc(match_expr(__e as __t));' ;
rewrite_expr 'marked!(__e as __t)' '__e' ;
delete_marks target ;

Diff #15

src/robotfindskitten.rs
203
    pub y: libc::c_int,
203
    pub y: libc::c_int,
204
    pub color: libc::c_int,
204
    pub color: libc::c_int,
205
    pub bold: bool,
205
    pub bold: bool,
206
    pub character: libc::c_char,
206
    pub character: libc::c_char,
207
}
207
}
208
static mut ver: &'static str =
208
static mut ver: &'static str = b"1.7320508.406\x00";
209
    b"1.7320508.406\x00" as *const u8 as *const libc::c_char as *mut libc::c_char;
210
#[inline]
209
#[inline]
211
unsafe extern "C" fn atoi(mut __nptr: *const libc::c_char) -> libc::c_int {
210
unsafe extern "C" fn atoi(mut __nptr: *const libc::c_char) -> libc::c_int {
212
    return strtol(
211
    return strtol(
213
        __nptr,
212
        __nptr,
214
        0 as *mut libc::c_void as *mut *mut libc::c_char,
213
        0 as *mut libc::c_void as *mut *mut libc::c_char,

Only the binary string literal remains, so we mark it and change it to an ordinary str:

select target 'item(ver); child(expr);' ;
bytestr_to_str ;
delete_marks target ;

Diff #16

src/robotfindskitten.rs
203
    pub y: libc::c_int,
203
    pub y: libc::c_int,
204
    pub color: libc::c_int,
204
    pub color: libc::c_int,
205
    pub bold: bool,
205
    pub bold: bool,
206
    pub character: libc::c_char,
206
    pub character: libc::c_char,
207
}
207
}
208
static mut ver: &'static str = b"1.7320508.406\x00";
208
static mut ver: &'static str = "1.7320508.406\u{0}";
209
#[inline]
209
#[inline]
210
unsafe extern "C" fn atoi(mut __nptr: *const libc::c_char) -> libc::c_int {
210
unsafe extern "C" fn atoi(mut __nptr: *const libc::c_char) -> libc::c_int {
211
    return strtol(
211
    return strtol(
212
        __nptr,
212
        __nptr,
213
        0 as *mut libc::c_void as *mut *mut libc::c_char,
213
        0 as *mut libc::c_void as *mut *mut libc::c_char,

Fixing ver's uses

ver's initializer is now well-typed, but its uses are still expecting a *mut c_char instead of a &str. To fix these up, we use the type_fix_rules command, which rewrites expressions anywhere a type error occurs:

type_fix_rules '*, &str, *const __t => __old.as_ptr()' ;

Diff #17

src/robotfindskitten.rs
1178
    let mut counter: libc::c_int = 0;
1178
    let mut counter: libc::c_int = 0;
1179
    fmt_mvprintw(
1179
    fmt_mvprintw(
1180
        0i32,
1180
        0i32,
1181
        0i32,
1181
        0i32,
1182
        format_args!("robotfindskitten v{:}\n\n", unsafe {
1182
        format_args!("robotfindskitten v{:}\n\n", unsafe {
1183
            ::std::ffi::CStr::from_ptr(ver as *const libc::c_char)
1183
            ::std::ffi::CStr::from_ptr(ver.as_ptr() as *const libc::c_char)
1184
                .to_str()
1184
                .to_str()
1185
                .unwrap()
1185
                .unwrap()
1186
        }),
1186
        }),
1187
    );
1187
    );
1188
    counter = 0i32;
1188
    counter = 0i32;

1251
    let mut dummy: libc::c_char = 0;
1251
    let mut dummy: libc::c_char = 0;
1252
    fmt_mvprintw(
1252
    fmt_mvprintw(
1253
        0i32,
1253
        0i32,
1254
        0i32,
1254
        0i32,
1255
        format_args!("robotfindskitten v{:}\n", unsafe {
1255
        format_args!("robotfindskitten v{:}\n", unsafe {
1256
            ::std::ffi::CStr::from_ptr(ver as *const libc::c_char)
1256
            ::std::ffi::CStr::from_ptr(ver.as_ptr() as *const libc::c_char)
1257
                .to_str()
1257
                .to_str()
1258
                .unwrap()
1258
                .unwrap()
1259
        }),
1259
        }),
1260
    );
1260
    );
1261
    fmt_printw(format_args!(
1261
    fmt_printw(format_args!(

Here we run type_fix_rules with only one rule: in any position (*), if an expression has type &str but is expected to have a raw pointer type (*const __t), then wrap the original expression in a call to .as_ptr(). This turns out to be enough to fix all the errors at uses of ver.

Making ver immutable

Now that all type errors have been corrected, we can finish our refactoring of ver. We make it immutable, then commit the changes.

select target 'item(ver);' ;
set_mutability imm ;

commit ;

Diff #18

src/robotfindskitten.rs
203
    pub y: libc::c_int,
203
    pub y: libc::c_int,
204
    pub color: libc::c_int,
204
    pub color: libc::c_int,
205
    pub bold: bool,
205
    pub bold: bool,
206
    pub character: libc::c_char,
206
    pub character: libc::c_char,
207
}
207
}
208
static mut ver: &'static str = "1.7320508.406\u{0}";
208
static ver: &'static str = "1.7320508.406\u{0}";
209
#[inline]
209
#[inline]
210
unsafe extern "C" fn atoi(mut __nptr: *const libc::c_char) -> libc::c_int {
210
unsafe extern "C" fn atoi(mut __nptr: *const libc::c_char) -> libc::c_int {
211
    return strtol(
211
    return strtol(
212
        __nptr,
212
        __nptr,
213
        0 as *mut libc::c_void as *mut *mut libc::c_char,
213
        0 as *mut libc::c_void as *mut *mut libc::c_char,

Static string array - messages

Aside from ver, robotfindskitten contains a static array of strings, called messages. Like ver, accessing messages is unsafe because each element is a raw *mut c_char pointer and because messages itself is a static mut.

We rewrite the type and initializer of messages using the same strategy as for ver:

select target 'item(messages); child(ty); desc(ty);' ;
rewrite_ty 'marked!(*mut libc::c_char)' "&'static str" ;
delete_marks target ;
select target 'item(messages); child(expr); desc(expr);' ;
rewrite_expr 'marked!(__e as __t)' '__e' ;
bytestr_to_str ;
delete_marks target ;

Diff #19

src/robotfindskitten.rs
218
will happen.*/
218
will happen.*/
219
/*Also, take note that robotfindskitten.c and configure.in
219
/*Also, take note that robotfindskitten.c and configure.in
220
currently have the version number hardcoded into them, and they
220
currently have the version number hardcoded into them, and they
221
should reflect MESSAGES. */
221
should reflect MESSAGES. */
222
/* Watch out for fenceposts.*/
222
/* Watch out for fenceposts.*/
223
static mut messages: [*mut libc::c_char; 406] = [
223
static mut messages: [&'static str; 406] = [
224
    b"\"I pity the fool who mistakes me for kitten!\", sez Mr. T.\x00" as *const u8
224
    "\"I pity the fool who mistakes me for kitten!\", sez Mr. T.\u{0}",
225
        as *const libc::c_char as *mut libc::c_char,
225
    "That\'s just an old tin can.\u{0}",
226
    b"That\'s just an old tin can.\x00" as *const u8 as *const libc::c_char as *mut libc::c_char,
226
    "It\'s an altar to the horse god.\u{0}",
227
    b"It\'s an altar to the horse god.\x00" as *const u8 as *const libc::c_char
228
        as *mut libc::c_char,
229
    b"A box of dancing mechanical pencils. They dance! They sing!\x00" as *const u8
227
    "A box of dancing mechanical pencils. They dance! They sing!\u{0}",
230
        as *const libc::c_char as *mut libc::c_char,
228
    "It\'s an old Duke Ellington record.\u{0}",
231
    b"It\'s an old Duke Ellington record.\x00" as *const u8 as *const libc::c_char
232
        as *mut libc::c_char,

933
        as *mut libc::c_char,
625
    "It\'s your favorite game -- robotfindscatan!\u{0}",
934
    b"It\'s your favorite game -- robotfindscatan!\x00" as *const u8 as *const libc::c_char
626
    "Just a man selling an albatross.\u{0}",
935
        as *mut libc::c_char,
627
    "The intermission from a 1930s silent movie.\u{0}",
936
    b"Just a man selling an albatross.\x00" as *const u8 as *const libc::c_char
628
    "It\'s an inverted billiard ball!\u{0}",
937
        as *mut libc::c_char,
629
    "The spectre of Sherlock Holmes wills you onwards.\u{0}",
938
    b"The intermission from a 1930s silent movie.\x00" as *const u8 as *const libc::c_char
939
        as *mut libc::c_char,
940
    b"It\'s an inverted billiard ball!\x00" as *const u8 as *const libc::c_char
941
        as *mut libc::c_char,
942
    b"The spectre of Sherlock Holmes wills you onwards.\x00" as *const u8 as *const libc::c_char
943
        as *mut libc::c_char,
944
];
630
];
945
/*
631
/*
946
 *Function definitions
632
 *Function definitions
947
 */
633
 */
948
/*Initialization and setup functions*/
634
/*Initialization and setup functions*/

We use type_fix_rules to fix up the uses of messages, as we did for ver:

type_fix_rules
    '*, &str, *const __t => __old.as_ptr()'
    '*, &str, *mut __t => __old.as_ptr() as *mut __t' ;

Diff #20

src/robotfindskitten.rs
1069
            }
1069
            }
1070
            _ => {
1070
            _ => {
1071
                message(
1071
                message(
1072
                    messages[bogus_messages[(*(*screen.offset(check_x as isize))
1072
                    messages[bogus_messages[(*(*screen.offset(check_x as isize))
1073
                        .offset(check_y as isize)
1073
                        .offset(check_y as isize)
1074
                        - 2i32) as usize] as usize],
1074
                        - 2i32) as usize] as usize]
1075
                        .as_ptr() as *mut i8,
1075
                );
1076
                );
1076
            }
1077
            }
1077
        }
1078
        }
1078
        return;
1079
        return;
1079
    }
1080
    }

Here we needed a second rule for *mut pointers, similar to the one for *const, because robotfindskitten mistakenly declares messages as an array of char* instead of const char*.

With all type errors fixed, we can make messages immutable and commit the changes:

select target 'item(messages);' ;
set_mutability imm ;

commit ;

Diff #21

src/robotfindskitten.rs
218
will happen.*/
218
will happen.*/
219
/*Also, take note that robotfindskitten.c and configure.in
219
/*Also, take note that robotfindskitten.c and configure.in
220
currently have the version number hardcoded into them, and they
220
currently have the version number hardcoded into them, and they
221
should reflect MESSAGES. */
221
should reflect MESSAGES. */
222
/* Watch out for fenceposts.*/
222
/* Watch out for fenceposts.*/
223
static mut messages: [&'static str; 406] = [
223
static messages: [&'static str; 406] = [
224
    "\"I pity the fool who mistakes me for kitten!\", sez Mr. T.\u{0}",
224
    "\"I pity the fool who mistakes me for kitten!\", sez Mr. T.\u{0}",
225
    "That\'s just an old tin can.\u{0}",
225
    "That\'s just an old tin can.\u{0}",
226
    "It\'s an altar to the horse god.\u{0}",
226
    "It\'s an altar to the horse god.\u{0}",
227
    "A box of dancing mechanical pencils. They dance! They sing!\u{0}",
227
    "A box of dancing mechanical pencils. They dance! They sing!\u{0}",
228
    "It\'s an old Duke Ellington record.\u{0}",
228
    "It\'s an old Duke Ellington record.\u{0}",

Heap allocations

The screen variable stores a heap-allocated two-dimensional array, represented in C as an int**. In Rust, this becomes *mut *mut c_int, which is unsafe to access. We replace it with CArray<CArray<c_int>>, where CArray is a memory-safe collection type provided by the c2rust_runtime library. CArray is convenient for this purpose because it supports C-style initialization and access patterns (including pointer arithmetic) while still guaranteeing memory safety.

We actually perform the conversion from *mut to CArray in two steps. First, we replace *mut with the simpler CBlockPtr type, also defined in c2rust_runtime. CBlockPtr provides some limited bounds checking, but otherwise functions much like a raw pointer. It serves as a useful intermediate step, letting us fix up the differences between the raw-pointer and CArray APIs in two stages instead of attempting to do it all at once. Once screen has been fully converted to CBlockPtr<CBlockPtr<c_int>>, we finish the conversion to CArray in the second step.

As a preliminary, we need to add an import of the c2rust_runtime library:

select target 'crate;' ;
create_item 'extern crate c2rust_runtime;' inside ;

Diff #22

src/robotfindskitten.rs
5
    non_snake_case,
5
    non_snake_case,
6
    non_upper_case_globals,
6
    non_upper_case_globals,
7
    unused_mut
7
    unused_mut
8
)]
8
)]
9
#![feature(const_raw_ptr_to_usize_cast, extern_types, libc)]
9
#![feature(const_raw_ptr_to_usize_cast, extern_types, libc)]
10
extern crate c2rust_runtime;
10
extern crate libc;
11
extern crate libc;
11
extern "C" {
12
extern "C" {
12
    pub type ldat;
13
    pub type ldat;
13
    #[no_mangle]
14
    #[no_mangle]
14
    fn printf(_: *const libc::c_char, ...) -> libc::c_int;
15
    fn printf(_: *const libc::c_char, ...) -> libc::c_int;

Now we can proceed with the actual refactoring.

Converting to CBlockPtr

We further break down the transition from *mut *mut c_int to CBlockPtr<CBlockPtr<c_int>> into two steps, first converting the inner pointer (leaving the overall type as *mut CBlockPtr<c_int>) and then the outer. We change the type annotation first, as we did for var and messages:

select target 'item(screen); child(ty);' ;
rewrite_ty 'marked!(*mut *mut __t)'
    '*mut ::c2rust_runtime::CBlockPtr<__t>' ;

Diff #23

src/robotfindskitten.rs
759
/* This array contains our internal representation of the screen. The
759
/* This array contains our internal representation of the screen. The
760
array is bigger than it needs to be, as we don't need to keep track
760
array is bigger than it needs to be, as we don't need to keep track
761
of the first few rows of the screen. But that requires making an
761
of the first few rows of the screen. But that requires making an
762
offset function and using that everywhere. So not right now. */
762
offset function and using that everywhere. So not right now. */
763
#[no_mangle]
763
#[no_mangle]
764
pub static mut screen: *mut *mut libc::c_int =
764
pub static mut screen: *mut ::c2rust_runtime::CBlockPtr =
765
    0 as *const *mut libc::c_int as *mut *mut libc::c_int;
765
    0 as *const *mut libc::c_int as *mut *mut libc::c_int;
766
#[no_mangle]
766
#[no_mangle]
767
pub unsafe extern "C" fn initialize_robot() {
767
pub unsafe extern "C" fn initialize_robot() {
768
    robot.x = rand() % (COLS - 1i32) + 1i32;
768
    robot.x = rand() % (COLS - 1i32) + 1i32;
769
    robot.y = rand() % (LINES - 1i32 - 3i32 + 1i32) + 3i32;
769
    robot.y = rand() % (LINES - 1i32 - 3i32 + 1i32) + 3i32;

This introduces type errors, letting us easily find (and fix) related expressions using type_fix_rules:

type_fix_rules
    'rval, *mut __t, ::c2rust_runtime::CBlockPtr<__u> =>
        unsafe { ::c2rust_runtime::CBlockPtr::from_ptr(__old) }'
    'rval, *mut __t, *mut __u => __old as *mut __u'
    ;

Diff #24

src/robotfindskitten.rs
707
   };
707
   };
708
    let mut i: libc::c_int = 0i32;
708
    let mut i: libc::c_int = 0i32;
709
    screen = malloc(
709
    screen = malloc(
710
        (::std::mem::size_of::<*mut libc::c_int>() as libc::c_ulong)
710
        (::std::mem::size_of::<*mut libc::c_int>() as libc::c_ulong)
711
            .wrapping_mul((COLS - 1i32 + 1i32) as libc::c_ulong),
711
            .wrapping_mul((COLS - 1i32 + 1i32) as libc::c_ulong),
712
    ) as *mut *mut libc::c_int;
712
    ) as *mut *mut libc::c_int as *mut ::c2rust_runtime::CBlockPtr<i32>;
713
    i = 0i32;
713
    i = 0i32;
714
    while i < COLS - 1i32 + 1i32 {
714
    while i < COLS - 1i32 + 1i32 {
715
        let ref mut fresh0 = *screen.offset(i as isize);
715
        let ref mut fresh0 = *screen.offset(i as isize);
716
        *fresh0 = malloc(
716
        *fresh0 = unsafe {
717
            ::c2rust_runtime::CBlockPtr::from_ptr(malloc(
717
            (::std::mem::size_of::() as libc::c_ulong)
718
                (::std::mem::size_of::() as libc::c_ulong)
718
                .wrapping_mul((LINES - 1i32 + 1i32) as libc::c_ulong),
719
                    .wrapping_mul((LINES - 1i32 + 1i32) as libc::c_ulong),
719
        ) as *mut libc::c_int;
720
            ) as *mut libc::c_int)
721
        };
720
        i += 1
722
        i += 1
721
    }
723
    }
722
    empty.x = -1i32;
724
    empty.x = -1i32;
723
    empty.y = -1i32;
725
    empty.y = -1i32;
724
    empty.color = 0i32;
726
    empty.color = 0i32;

760
array is bigger than it needs to be, as we don't need to keep track
762
array is bigger than it needs to be, as we don't need to keep track
761
of the first few rows of the screen. But that requires making an
763
of the first few rows of the screen. But that requires making an
762
offset function and using that everywhere. So not right now. */
764
offset function and using that everywhere. So not right now. */
763
#[no_mangle]
765
#[no_mangle]
764
pub static mut screen: *mut ::c2rust_runtime::CBlockPtr =
766
pub static mut screen: *mut ::c2rust_runtime::CBlockPtr =
765
    0 as *const *mut libc::c_int as *mut *mut libc::c_int;
767
    0 as *const *mut libc::c_int as *mut *mut libc::c_int as *mut ::c2rust_runtime::CBlockPtr<i32>;
766
#[no_mangle]
768
#[no_mangle]
767
pub unsafe extern "C" fn initialize_robot() {
769
pub unsafe extern "C" fn initialize_robot() {
768
    robot.x = rand() % (COLS - 1i32) + 1i32;
770
    robot.x = rand() % (COLS - 1i32) + 1i32;
769
    robot.y = rand() % (LINES - 1i32 - 3i32 + 1i32) + 3i32;
771
    robot.y = rand() % (LINES - 1i32 - 3i32 + 1i32) + 3i32;
770
    robot.character = '#' as i32 as libc::c_char;
772
    robot.character = '#' as i32 as libc::c_char;

The first rule provided here handles the later part of screen's initialization, where the program allocates a *mut c_int array (now CBlockPtr<c_int>) for each row of the screen. The second rule handles the earlier part, where it allocates the top-level *mut *mut c_int (now *mut CBlockPtr<c_int>). Both allocations now need a cast, since the type of the rows has changed.

One category of type errors remains: the initialization code tries to dereference the result of offsetting the array pointer, which is not possible directly with the CBlockPtr API. We add the necessary method call using rewrite_expr:

rewrite_expr
    '*typed!(__e, ::c2rust_runtime::block_ptr::CBlockOffset<__t>)'
    '*__e.as_mut()' ;

Diff #25

src/robotfindskitten.rs
728
   empty.character = ' ' as i32 as libc::c_char;
728
   empty.character = ' ' as i32 as libc::c_char;
729
    counter = 0i32;
729
    counter = 0i32;
730
    while counter <= COLS - 1i32 {
730
    while counter <= COLS - 1i32 {
731
        counter2 = 0i32;
731
        counter2 = 0i32;
732
        while counter2 <= LINES - 1i32 {
732
        while counter2 <= LINES - 1i32 {
733
            *(*screen.offset(counter as isize)).offset(counter2 as isize) = -1i32;
733
            *(*screen.offset(counter as isize))
734
                .offset(counter2 as isize)
735
                .as_mut() = -1i32;
734
            counter2 += 1
736
            counter2 += 1
735
        }
737
        }
736
        counter += 1
738
        counter += 1
737
    }
739
    }
738
    counter = 0i32;
740
    counter = 0i32;

770
   robot.x = rand() % (COLS - 1i32) + 1i32;
772
   robot.x = rand() % (COLS - 1i32) + 1i32;
771
    robot.y = rand() % (LINES - 1i32 - 3i32 + 1i32) + 3i32;
773
    robot.y = rand() % (LINES - 1i32 - 3i32 + 1i32) + 3i32;
772
    robot.character = '#' as i32 as libc::c_char;
774
    robot.character = '#' as i32 as libc::c_char;
773
    robot.color = 0i32;
775
    robot.color = 0i32;
774
    robot.bold = 0 != 0i32;
776
    robot.bold = 0 != 0i32;
775
    *(*screen.offset(robot.x as isize)).offset(robot.y as isize) = 0i32;
777
    *(*screen.offset(robot.x as isize))
778
        .offset(robot.y as isize)
779
        .as_mut() = 0i32;
776
}
780
}
777
/*Global variables. Bite me, it's fun.*/
781
/*Global variables. Bite me, it's fun.*/
778
#[no_mangle]
782
#[no_mangle]
779
pub static mut robot: screen_object = screen_object {
783
pub static mut robot: screen_object = screen_object {
780
    x: 0,
784
    x: 0,

786
#[no_mangle]
790
#[no_mangle]
787
pub unsafe extern "C" fn initialize_kitten() {
791
pub unsafe extern "C" fn initialize_kitten() {
788
    loop {
792
    loop {
789
        kitten.x = rand() % (COLS - 1i32) + 1i32;
793
        kitten.x = rand() % (COLS - 1i32) + 1i32;
790
        kitten.y = rand() % (LINES - 1i32 - 3i32 + 1i32) + 3i32;
794
        kitten.y = rand() % (LINES - 1i32 - 3i32 + 1i32) + 3i32;
791
        if !(*(*screen.offset(kitten.x as isize)).offset(kitten.y as isize) != -1i32) {
795
        if !(*(*screen.offset(kitten.x as isize))
796
            .offset(kitten.y as isize)
797
            .as_mut()
798
            != -1i32)
799
        {
792
            break;
800
            break;
793
        }
801
        }
794
    }
802
    }
795
    loop {
803
    loop {
796
        kitten.character = (rand() % (126i32 - '!' as i32 + 1i32) + '!' as i32) as libc::c_char;
804
        kitten.character = (rand() % (126i32 - '!' as i32 + 1i32) + '!' as i32) as libc::c_char;
797
        if !(0 == validchar(kitten.character)) {
805
        if !(0 == validchar(kitten.character)) {
798
            break;
806
            break;
799
        }
807
        }
800
    }
808
    }
801
    *(*screen.offset(kitten.x as isize)).offset(kitten.y as isize) = 1i32;
809
    *(*screen.offset(kitten.x as isize))
810
        .offset(kitten.y as isize)
811
        .as_mut() = 1i32;
802
    kitten.color = rand() % 6i32 + 1i32;
812
    kitten.color = rand() % 6i32 + 1i32;
803
    kitten.bold = 0 != if 0 != rand() % 2i32 { 1i32 } else { 0i32 };
813
    kitten.bold = 0 != if 0 != rand() % 2i32 { 1i32 } else { 0i32 };
804
}
814
}
805
#[no_mangle]
815
#[no_mangle]
806
pub static mut kitten: screen_object = screen_object {
816
pub static mut kitten: screen_object = screen_object {

837
       loop {
847
       loop {
838
            bogus[counter as usize].x = rand() % (COLS - 1i32) + 1i32;
848
            bogus[counter as usize].x = rand() % (COLS - 1i32) + 1i32;
839
            bogus[counter as usize].y = rand() % (LINES - 1i32 - 3i32 + 1i32) + 3i32;
849
            bogus[counter as usize].y = rand() % (LINES - 1i32 - 3i32 + 1i32) + 3i32;
840
            if !(*(*screen.offset(bogus[counter as usize].x as isize))
850
            if !(*(*screen.offset(bogus[counter as usize].x as isize))
841
                .offset(bogus[counter as usize].y as isize)
851
                .offset(bogus[counter as usize].y as isize)
852
                .as_mut()
842
                != -1i32)
853
                != -1i32)
843
            {
854
            {
844
                break;
855
                break;
845
            }
856
            }
846
        }
857
        }
847
        *(*screen.offset(bogus[counter as usize].x as isize))
858
        *(*screen.offset(bogus[counter as usize].x as isize))
848
            .offset(bogus[counter as usize].y as isize) = counter + 2i32;
859
            .offset(bogus[counter as usize].y as isize)
860
            .as_mut() = counter + 2i32;
849
        loop {
861
        loop {
850
            index = (rand() as libc::c_ulong).wrapping_rem(
862
            index = (rand() as libc::c_ulong).wrapping_rem(
851
                (::std::mem::size_of::<[*mut libc::c_char; 406]>() as libc::c_ulong)
863
                (::std::mem::size_of::<[*mut libc::c_char; 406]>() as libc::c_ulong)
852
                    .wrapping_div(::std::mem::size_of::<*mut libc::c_char>() as libc::c_ulong),
864
                    .wrapping_div(::std::mem::size_of::<*mut libc::c_char>() as libc::c_ulong),
853
            ) as libc::c_int;
865
            ) as libc::c_int;

989
       if !(old_x == robot.x && old_y == robot.y) {
1001
       if !(old_x == robot.x && old_y == robot.y) {
990
            if wmove(stdscr, old_y, old_x) == -1i32 {
1002
            if wmove(stdscr, old_y, old_x) == -1i32 {
991
            } else {
1003
            } else {
992
                waddch(stdscr, ' ' as i32 as chtype);
1004
                waddch(stdscr, ' ' as i32 as chtype);
993
            };
1005
            };
994
            *(*screen.offset(old_x as isize)).offset(old_y as isize) = -1i32;
1006
            *(*screen.offset(old_x as isize))
1007
                .offset(old_y as isize)
1008
                .as_mut() = -1i32;
995
            draw(robot);
1009
            draw(robot);
996
            wrefresh(stdscr);
1010
            wrefresh(stdscr);
997
            *(*screen.offset(robot.x as isize)).offset(robot.y as isize) = 0i32;
1011
            *(*screen.offset(robot.x as isize))
1012
                .offset(robot.y as isize)
1013
                .as_mut() = 0i32;
998
            old_x = robot.x;
1014
            old_x = robot.x;
999
            old_y = robot.y
1015
            old_y = robot.y
1000
        }
1016
        }
1001
        input = wgetch(stdscr)
1017
        input = wgetch(stdscr)
1002
    }
1018
    }

1058
       }
1074
       }
1059
    }
1075
    }
1060
    if check_y < 3i32 || check_y > LINES - 1i32 || check_x < 0i32 || check_x > COLS - 1i32 {
1076
    if check_y < 3i32 || check_y > LINES - 1i32 || check_x < 0i32 || check_x > COLS - 1i32 {
1061
        return;
1077
        return;
1062
    }
1078
    }
1063
    if *(*screen.offset(check_x as isize)).offset(check_y as isize) != -1i32 {
1079
    if *(*screen.offset(check_x as isize))
1080
        .offset(check_y as isize)
1081
        .as_mut()
1082
        != -1i32
1083
    {
1064
        match *(*screen.offset(check_x as isize)).offset(check_y as isize) {
1084
        match *(*screen.offset(check_x as isize))
1085
            .offset(check_y as isize)
1086
            .as_mut()
1087
        {
1065
            0 => {}
1088
            0 => {}
1066
            1 => {
1089
            1 => {
1067
                /*We didn't move, or we're stuck in a
1090
                /*We didn't move, or we're stuck in a
1068
                time warp or something.*/
1091
                time warp or something.*/
1069
                wmove(stdscr, 1i32, 0i32);
1092
                wmove(stdscr, 1i32, 0i32);

1072
           }
1095
           }
1073
            _ => {
1096
            _ => {
1074
                message(
1097
                message(
1075
                    messages[bogus_messages[(*(*screen.offset(check_x as isize))
1098
                    messages[bogus_messages[(*(*screen.offset(check_x as isize))
1076
                        .offset(check_y as isize)
1099
                        .offset(check_y as isize)
1100
                        .as_mut()
1077
                        - 2i32) as usize] as usize]
1101
                        - 2i32) as usize] as usize]
1078
                        .as_ptr() as *mut i8,
1102
                        .as_ptr() as *mut i8,
1079
                );
1103
                );
1080
            }
1104
            }
1081
        }
1105
        }

Here, the pattern filters for dereferences of CBlockOffset expressions, which result from calling offset on a CBlockPtr, and adds a call to as_mut() before the dereference.

The conversion of screen to *mut CBlockPtr<c_int> is now complete. The conversion to CBlockPtr<CBlockPtr<c_int>> uses a similar refactoring script:

select target 'crate; item(screen); child(ty);' ;
rewrite_ty 'marked!(*mut __t)'
    '::c2rust_runtime::CBlockPtr<__t>' ;
type_fix_rules
    'rval, *mut __t, ::c2rust_runtime::CBlockPtr<__u> =>
        unsafe { ::c2rust_runtime::CBlockPtr::from_ptr(__old) }'
    'rval, *mut __t, *mut __u => __old as *mut __u'
    ;
rewrite_expr
    '*typed!(__e, ::c2rust_runtime::block_ptr::CBlockOffset<__t>)'
    '*__e.as_mut()' ;

Diff #26

src/robotfindskitten.rs
704
       color: 0,
704
       color: 0,
705
        bold: false,
705
        bold: false,
706
        character: 0,
706
        character: 0,
707
    };
707
    };
708
    let mut i: libc::c_int = 0i32;
708
    let mut i: libc::c_int = 0i32;
709
    screen = malloc(
709
    screen = unsafe {
710
        ::c2rust_runtime::CBlockPtr::from_ptr(malloc(
710
        (::std::mem::size_of::<*mut libc::c_int>() as libc::c_ulong)
711
            (::std::mem::size_of::<*mut libc::c_int>() as libc::c_ulong)
711
            .wrapping_mul((COLS - 1i32 + 1i32) as libc::c_ulong),
712
                .wrapping_mul((COLS - 1i32 + 1i32) as libc::c_ulong),
712
    ) as *mut *mut libc::c_int as *mut ::c2rust_runtime::CBlockPtr<i32>;
713
        ) as *mut *mut libc::c_int
714
            as *mut ::c2rust_runtime::CBlockPtr<i32>)
715
    };
713
    i = 0i32;
716
    i = 0i32;
714
    while i < COLS - 1i32 + 1i32 {
717
    while i < COLS - 1i32 + 1i32 {
715
        let ref mut fresh0 = *screen.offset(i as isize);
718
        let ref mut fresh0 = *screen.offset(i as isize).as_mut();
716
        *fresh0 = unsafe {
719
        *fresh0 = unsafe {
717
            ::c2rust_runtime::CBlockPtr::from_ptr(malloc(
720
            ::c2rust_runtime::CBlockPtr::from_ptr(malloc(
718
                (::std::mem::size_of::() as libc::c_ulong)
721
                (::std::mem::size_of::() as libc::c_ulong)
719
                    .wrapping_mul((LINES - 1i32 + 1i32) as libc::c_ulong),
722
                    .wrapping_mul((LINES - 1i32 + 1i32) as libc::c_ulong),
720
            ) as *mut libc::c_int)
723
            ) as *mut libc::c_int)

728
   empty.character = ' ' as i32 as libc::c_char;
731
   empty.character = ' ' as i32 as libc::c_char;
729
    counter = 0i32;
732
    counter = 0i32;
730
    while counter <= COLS - 1i32 {
733
    while counter <= COLS - 1i32 {
731
        counter2 = 0i32;
734
        counter2 = 0i32;
732
        while counter2 <= LINES - 1i32 {
735
        while counter2 <= LINES - 1i32 {
733
            *(*screen.offset(counter as isize))
736
            *(*screen.offset(counter as isize).as_mut())
734
                .offset(counter2 as isize)
737
                .offset(counter2 as isize)
735
                .as_mut() = -1i32;
738
                .as_mut() = -1i32;
736
            counter2 += 1
739
            counter2 += 1
737
        }
740
        }
738
        counter += 1
741
        counter += 1

763
/* This array contains our internal representation of the screen. The
766
/* This array contains our internal representation of the screen. The
764
array is bigger than it needs to be, as we don't need to keep track
767
array is bigger than it needs to be, as we don't need to keep track
765
of the first few rows of the screen. But that requires making an
768
of the first few rows of the screen. But that requires making an
766
offset function and using that everywhere. So not right now. */
769
offset function and using that everywhere. So not right now. */
767
#[no_mangle]
770
#[no_mangle]
768
pub static mut screen: *mut ::c2rust_runtime::CBlockPtr =
771
pub static mut screen: ::c2rust_runtime::CBlockPtr<::c2rust_runtime::CBlockPtr> = unsafe {
769
    0 as *const *mut libc::c_int as *mut *mut libc::c_int as *mut ::c2rust_runtime::CBlockPtr<i32>;
772
    ::c2rust_runtime::CBlockPtr::from_ptr(
773
        0 as *const *mut libc::c_int as *mut *mut libc::c_int
774
            as *mut ::c2rust_runtime::CBlockPtr<i32>,
775
    )
776
};
770
#[no_mangle]
777
#[no_mangle]
771
pub unsafe extern "C" fn initialize_robot() {
778
pub unsafe extern "C" fn initialize_robot() {
772
    robot.x = rand() % (COLS - 1i32) + 1i32;
779
    robot.x = rand() % (COLS - 1i32) + 1i32;
773
    robot.y = rand() % (LINES - 1i32 - 3i32 + 1i32) + 3i32;
780
    robot.y = rand() % (LINES - 1i32 - 3i32 + 1i32) + 3i32;
774
    robot.character = '#' as i32 as libc::c_char;
781
    robot.character = '#' as i32 as libc::c_char;
775
    robot.color = 0i32;
782
    robot.color = 0i32;
776
    robot.bold = 0 != 0i32;
783
    robot.bold = 0 != 0i32;
777
    *(*screen.offset(robot.x as isize))
784
    *(*screen.offset(robot.x as isize).as_mut())
778
        .offset(robot.y as isize)
785
        .offset(robot.y as isize)
779
        .as_mut() = 0i32;
786
        .as_mut() = 0i32;
780
}
787
}
781
/*Global variables. Bite me, it's fun.*/
788
/*Global variables. Bite me, it's fun.*/
782
#[no_mangle]
789
#[no_mangle]

790
#[no_mangle]
797
#[no_mangle]
791
pub unsafe extern "C" fn initialize_kitten() {
798
pub unsafe extern "C" fn initialize_kitten() {
792
    loop {
799
    loop {
793
        kitten.x = rand() % (COLS - 1i32) + 1i32;
800
        kitten.x = rand() % (COLS - 1i32) + 1i32;
794
        kitten.y = rand() % (LINES - 1i32 - 3i32 + 1i32) + 3i32;
801
        kitten.y = rand() % (LINES - 1i32 - 3i32 + 1i32) + 3i32;
795
        if !(*(*screen.offset(kitten.x as isize))
802
        if !(*(*screen.offset(kitten.x as isize).as_mut())
796
            .offset(kitten.y as isize)
803
            .offset(kitten.y as isize)
797
            .as_mut()
804
            .as_mut()
798
            != -1i32)
805
            != -1i32)
799
        {
806
        {
800
            break;
807
            break;

804
       kitten.character = (rand() % (126i32 - '!' as i32 + 1i32) + '!' as i32) as libc::c_char;
811
       kitten.character = (rand() % (126i32 - '!' as i32 + 1i32) + '!' as i32) as libc::c_char;
805
        if !(0 == validchar(kitten.character)) {
812
        if !(0 == validchar(kitten.character)) {
806
            break;
813
            break;
807
        }
814
        }
808
    }
815
    }
809
    *(*screen.offset(kitten.x as isize))
816
    *(*screen.offset(kitten.x as isize).as_mut())
810
        .offset(kitten.y as isize)
817
        .offset(kitten.y as isize)
811
        .as_mut() = 1i32;
818
        .as_mut() = 1i32;
812
    kitten.color = rand() % 6i32 + 1i32;
819
    kitten.color = rand() % 6i32 + 1i32;
813
    kitten.bold = 0 != if 0 != rand() % 2i32 { 1i32 } else { 0i32 };
820
    kitten.bold = 0 != if 0 != rand() % 2i32 { 1i32 } else { 0i32 };
814
}
821
}

845
           }
852
           }
846
        }
853
        }
847
        loop {
854
        loop {
848
            bogus[counter as usize].x = rand() % (COLS - 1i32) + 1i32;
855
            bogus[counter as usize].x = rand() % (COLS - 1i32) + 1i32;
849
            bogus[counter as usize].y = rand() % (LINES - 1i32 - 3i32 + 1i32) + 3i32;
856
            bogus[counter as usize].y = rand() % (LINES - 1i32 - 3i32 + 1i32) + 3i32;
850
            if !(*(*screen.offset(bogus[counter as usize].x as isize))
857
            if !(*(*screen.offset(bogus[counter as usize].x as isize).as_mut())
851
                .offset(bogus[counter as usize].y as isize)
858
                .offset(bogus[counter as usize].y as isize)
852
                .as_mut()
859
                .as_mut()
853
                != -1i32)
860
                != -1i32)
854
            {
861
            {
855
                break;
862
                break;
856
            }
863
            }
857
        }
864
        }
858
        *(*screen.offset(bogus[counter as usize].x as isize))
865
        *(*screen.offset(bogus[counter as usize].x as isize).as_mut())
859
            .offset(bogus[counter as usize].y as isize)
866
            .offset(bogus[counter as usize].y as isize)
860
            .as_mut() = counter + 2i32;
867
            .as_mut() = counter + 2i32;
861
        loop {
868
        loop {
862
            index = (rand() as libc::c_ulong).wrapping_rem(
869
            index = (rand() as libc::c_ulong).wrapping_rem(
863
                (::std::mem::size_of::<[*mut libc::c_char; 406]>() as libc::c_ulong)
870
                (::std::mem::size_of::<[*mut libc::c_char; 406]>() as libc::c_ulong)

1001
       if !(old_x == robot.x && old_y == robot.y) {
1008
       if !(old_x == robot.x && old_y == robot.y) {
1002
            if wmove(stdscr, old_y, old_x) == -1i32 {
1009
            if wmove(stdscr, old_y, old_x) == -1i32 {
1003
            } else {
1010
            } else {
1004
                waddch(stdscr, ' ' as i32 as chtype);
1011
                waddch(stdscr, ' ' as i32 as chtype);
1005
            };
1012
            };
1006
            *(*screen.offset(old_x as isize))
1013
            *(*screen.offset(old_x as isize).as_mut())
1007
                .offset(old_y as isize)
1014
                .offset(old_y as isize)
1008
                .as_mut() = -1i32;
1015
                .as_mut() = -1i32;
1009
            draw(robot);
1016
            draw(robot);
1010
            wrefresh(stdscr);
1017
            wrefresh(stdscr);
1011
            *(*screen.offset(robot.x as isize))
1018
            *(*screen.offset(robot.x as isize).as_mut())
1012
                .offset(robot.y as isize)
1019
                .offset(robot.y as isize)
1013
                .as_mut() = 0i32;
1020
                .as_mut() = 0i32;
1014
            old_x = robot.x;
1021
            old_x = robot.x;
1015
            old_y = robot.y
1022
            old_y = robot.y
1016
        }
1023
        }

1074
       }
1081
       }
1075
    }
1082
    }
1076
    if check_y < 3i32 || check_y > LINES - 1i32 || check_x < 0i32 || check_x > COLS - 1i32 {
1083
    if check_y < 3i32 || check_y > LINES - 1i32 || check_x < 0i32 || check_x > COLS - 1i32 {
1077
        return;
1084
        return;
1078
    }
1085
    }
1079
    if *(*screen.offset(check_x as isize))
1086
    if *(*screen.offset(check_x as isize).as_mut())
1080
        .offset(check_y as isize)
1087
        .offset(check_y as isize)
1081
        .as_mut()
1088
        .as_mut()
1082
        != -1i32
1089
        != -1i32
1083
    {
1090
    {
1084
        match *(*screen.offset(check_x as isize))
1091
        match *(*screen.offset(check_x as isize).as_mut())
1085
            .offset(check_y as isize)
1092
            .offset(check_y as isize)
1086
            .as_mut()
1093
            .as_mut()
1087
        {
1094
        {
1088
            0 => {}
1095
            0 => {}
1089
            1 => {
1096
            1 => {

1093
               wclrtoeol(stdscr);
1100
               wclrtoeol(stdscr);
1094
                play_animation(input);
1101
                play_animation(input);
1095
            }
1102
            }
1096
            _ => {
1103
            _ => {
1097
                message(
1104
                message(
1098
                    messages[bogus_messages[(*(*screen.offset(check_x as isize))
1105
                    messages[bogus_messages[(*(*screen.offset(check_x as isize).as_mut())
1099
                        .offset(check_y as isize)
1106
                        .offset(check_y as isize)
1100
                        .as_mut()
1107
                        .as_mut()
1101
                        - 2i32) as usize] as usize]
1108
                        - 2i32) as usize] as usize]
1102
                        .as_ptr() as *mut i8,
1109
                        .as_ptr() as *mut i8,
1103
                );
1110
                );

The only change is in the rewrite_ty step.

There's one last bit of cleanup to perform: now that screen has the desired CBlockPtr<CBlockPtr<c_int>> type, we can rewrite the allocations that initialize it. At this point the allocations use the unsafe malloc function followed by the unsafe CBlockPtr::from_ptr, but we can change that to use the safe CBlockPtr::alloc method instead:

rewrite_expr 'malloc(__e) as *mut __t as *mut __u' 'malloc(__e) as *mut __u' ;
rewrite_expr
    '::c2rust_runtime::CBlockPtr::from_ptr(malloc(__e) as *mut __t)'
    '::c2rust_runtime::CBlockPtr::alloc(
        __e as usize / ::std::mem::size_of::<__t>())'
    ;

Diff #27

src/robotfindskitten.rs
705
       bold: false,
705
       bold: false,
706
        character: 0,
706
        character: 0,
707
    };
707
    };
708
    let mut i: libc::c_int = 0i32;
708
    let mut i: libc::c_int = 0i32;
709
    screen = unsafe {
709
    screen = unsafe {
710
        ::c2rust_runtime::CBlockPtr::from_ptr(malloc(
710
        ::c2rust_runtime::CBlockPtr::alloc(
711
            (::std::mem::size_of::<*mut libc::c_int>() as libc::c_ulong)
711
            (::std::mem::size_of::<*mut libc::c_int>() as libc::c_ulong)
712
                .wrapping_mul((COLS - 1i32 + 1i32) as libc::c_ulong),
712
                .wrapping_mul((COLS - 1i32 + 1i32) as libc::c_ulong) as usize
713
        ) as *mut *mut libc::c_int
713
                / ::std::mem::size_of::<::c2rust_runtime::CBlockPtr<i32>>(),
714
            as *mut ::c2rust_runtime::CBlockPtr<i32>)
714
        )
715
    };
715
    };
716
    i = 0i32;
716
    i = 0i32;
717
    while i < COLS - 1i32 + 1i32 {
717
    while i < COLS - 1i32 + 1i32 {
718
        let ref mut fresh0 = *screen.offset(i as isize).as_mut();
718
        let ref mut fresh0 = *screen.offset(i as isize).as_mut();
719
        *fresh0 = unsafe {
719
        *fresh0 = unsafe {
720
            ::c2rust_runtime::CBlockPtr::from_ptr(malloc(
720
            ::c2rust_runtime::CBlockPtr::alloc(
721
                (::std::mem::size_of::() as libc::c_ulong)
721
                (::std::mem::size_of::() as libc::c_ulong)
722
                    .wrapping_mul((LINES - 1i32 + 1i32) as libc::c_ulong),
722
                    .wrapping_mul((LINES - 1i32 + 1i32) as libc::c_ulong) as usize
723
            ) as *mut libc::c_int)
723
                    / ::std::mem::size_of::(),
724
            )
724
        };
725
        };
725
        i += 1
726
        i += 1
726
    }
727
    }
727
    empty.x = -1i32;
728
    empty.x = -1i32;
728
    empty.y = -1i32;
729
    empty.y = -1i32;

This doesn't remove the unsafe blocks wrapping each allocation - we leave those until the end of our refactoring, when we remove unnecessary unsafe blocks throughout the entire crate at once.

At this point, the refactoring of screen to is done, and we can commit the changes:

commit ;

Diff #28

src/robotfindskitten.rs
7
    unused_mut
7
    unused_mut
8
)]
8
)]
9
#![feature(const_raw_ptr_to_usize_cast, extern_types, libc)]
9
#![feature(const_raw_ptr_to_usize_cast, extern_types, libc)]
10
extern crate c2rust_runtime;
10
extern crate c2rust_runtime;
11
extern crate libc;
11
extern crate libc;
12
extern "C" {
12
extern "C" {
13
    pub type ldat;
13
    pub type ldat;
14
    #[no_mangle]
14
    #[no_mangle]
15
    fn printf(_: *const libc::c_char, ...) -> libc::c_int;
15
    fn printf(_: *const libc::c_char, ...) -> libc::c_int;

Converting to CArray

The CArray and CBlockPtr APIs are deliberately quite similar, which makes this part of the screen refactoring fairly straightforward.

First, we replace all uses of CBlockPtr with CArray, both in types and in function calls:

rewrite_ty '::c2rust_runtime::CBlockPtr<__t>' '::c2rust_runtime::CArray<__t>' ;
rewrite_expr
    '::c2rust_runtime::CBlockPtr::from_ptr'
    '::c2rust_runtime::CArray::from_ptr' ;
rewrite_expr
    '::c2rust_runtime::CBlockPtr::alloc'
    '::c2rust_runtime::CArray::alloc' ;

Diff #29

src/robotfindskitten.rs
705
        bold: false,
705
        bold: false,
706
        character: 0,
706
        character: 0,
707
    };
707
    };
708
    let mut i: libc::c_int = 0i32;
708
    let mut i: libc::c_int = 0i32;
709
    screen = unsafe {
709
    screen = unsafe {
710
        ::c2rust_runtime::CBlockPtr::alloc(
710
        ::c2rust_runtime::CArray::alloc(
711
            (::std::mem::size_of::<*mut libc::c_int>() as libc::c_ulong)
711
            (::std::mem::size_of::<*mut libc::c_int>() as libc::c_ulong)
712
                .wrapping_mul((COLS - 1i32 + 1i32) as libc::c_ulong) as usize
712
                .wrapping_mul((COLS - 1i32 + 1i32) as libc::c_ulong) as usize
713
                / ::std::mem::size_of::<::c2rust_runtime::CBlockPtr<i32>>(),
713
                / ::std::mem::size_of::<::c2rust_runtime::CArray<i32>>(),
714
        )
714
        )
715
    };
715
    };
716
    i = 0i32;
716
    i = 0i32;
717
    while i < COLS - 1i32 + 1i32 {
717
    while i < COLS - 1i32 + 1i32 {
718
        let ref mut fresh0 = *screen.offset(i as isize).as_mut();
718
        let ref mut fresh0 = *screen.offset(i as isize).as_mut();
719
        *fresh0 = unsafe {
719
        *fresh0 = unsafe {
720
            ::c2rust_runtime::CBlockPtr::alloc(
720
            ::c2rust_runtime::CArray::alloc(
721
                (::std::mem::size_of::() as libc::c_ulong)
721
                (::std::mem::size_of::() as libc::c_ulong)
722
                    .wrapping_mul((LINES - 1i32 + 1i32) as libc::c_ulong) as usize
722
                    .wrapping_mul((LINES - 1i32 + 1i32) as libc::c_ulong) as usize
723
                    / ::std::mem::size_of::(),
723
                    / ::std::mem::size_of::(),
724
            )
724
            )
725
        };
725
        };

767
/* This array contains our internal representation of the screen. The
767
/* This array contains our internal representation of the screen. The
768
array is bigger than it needs to be, as we don't need to keep track
768
array is bigger than it needs to be, as we don't need to keep track
769
of the first few rows of the screen. But that requires making an
769
of the first few rows of the screen. But that requires making an
770
offset function and using that everywhere. So not right now. */
770
offset function and using that everywhere. So not right now. */
771
#[no_mangle]
771
#[no_mangle]
772
pub static mut screen: ::c2rust_runtime::CBlockPtr<::c2rust_runtime::CBlockPtr> = unsafe {
772
pub static mut screen: ::c2rust_runtime::CArray<::c2rust_runtime::CArray> = unsafe {
773
    ::c2rust_runtime::CBlockPtr::from_ptr(
773
    ::c2rust_runtime::CArray::from_ptr(
774
        0 as *const *mut libc::c_int as *mut *mut libc::c_int
774
        0 as *const *mut libc::c_int as *mut *mut libc::c_int as *mut ::c2rust_runtime::CArray<i32>,
775
            as *mut ::c2rust_runtime::CBlockPtr<i32>,
776
    )
775
    )
777
};
776
};
778
#[no_mangle]
777
#[no_mangle]
779
pub unsafe extern "C" fn initialize_robot() {
778
pub unsafe extern "C" fn initialize_robot() {
780
    robot.x = rand() % (COLS - 1i32) + 1i32;
779
    robot.x = rand() % (COLS - 1i32) + 1i32;

Next, we fix up calls to offset. Unlike CBlockPtr (and raw pointers in general), CArray distinguishes between mutable and immutable offset pointers. We handle this by simply replacing all offset calls with offset_mut:

rewrite_expr
    'typed!(__e, ::c2rust_runtime::CArray<__t>).offset(__f)'
    '__e.offset_mut(__f)' ;

Diff #30

src/robotfindskitten.rs
713
                / ::std::mem::size_of::<::c2rust_runtime::CArray<i32>>(),
713
                / ::std::mem::size_of::<::c2rust_runtime::CArray<i32>>(),
714
        )
714
        )
715
    };
715
    };
716
    i = 0i32;
716
    i = 0i32;
717
    while i < COLS - 1i32 + 1i32 {
717
    while i < COLS - 1i32 + 1i32 {
718
        let ref mut fresh0 = *screen.offset(i as isize).as_mut();
718
        let ref mut fresh0 = *screen.offset_mut(i as isize).as_mut();
719
        *fresh0 = unsafe {
719
        *fresh0 = unsafe {
720
            ::c2rust_runtime::CArray::alloc(
720
            ::c2rust_runtime::CArray::alloc(
721
                (::std::mem::size_of::() as libc::c_ulong)
721
                (::std::mem::size_of::() as libc::c_ulong)
722
                    .wrapping_mul((LINES - 1i32 + 1i32) as libc::c_ulong) as usize
722
                    .wrapping_mul((LINES - 1i32 + 1i32) as libc::c_ulong) as usize
723
                    / ::std::mem::size_of::(),
723
                    / ::std::mem::size_of::(),

732
    empty.character = ' ' as i32 as libc::c_char;
732
    empty.character = ' ' as i32 as libc::c_char;
733
    counter = 0i32;
733
    counter = 0i32;
734
    while counter <= COLS - 1i32 {
734
    while counter <= COLS - 1i32 {
735
        counter2 = 0i32;
735
        counter2 = 0i32;
736
        while counter2 <= LINES - 1i32 {
736
        while counter2 <= LINES - 1i32 {
737
            *(*screen.offset(counter as isize).as_mut())
737
            *(*screen.offset_mut(counter as isize).as_mut())
738
                .offset(counter2 as isize)
738
                .offset_mut(counter2 as isize)
739
                .as_mut() = -1i32;
739
                .as_mut() = -1i32;
740
            counter2 += 1
740
            counter2 += 1
741
        }
741
        }
742
        counter += 1
742
        counter += 1
743
    }
743
    }

779
    robot.x = rand() % (COLS - 1i32) + 1i32;
779
    robot.x = rand() % (COLS - 1i32) + 1i32;
780
    robot.y = rand() % (LINES - 1i32 - 3i32 + 1i32) + 3i32;
780
    robot.y = rand() % (LINES - 1i32 - 3i32 + 1i32) + 3i32;
781
    robot.character = '#' as i32 as libc::c_char;
781
    robot.character = '#' as i32 as libc::c_char;
782
    robot.color = 0i32;
782
    robot.color = 0i32;
783
    robot.bold = 0 != 0i32;
783
    robot.bold = 0 != 0i32;
784
    *(*screen.offset(robot.x as isize).as_mut())
784
    *(*screen.offset_mut(robot.x as isize).as_mut())
785
        .offset(robot.y as isize)
785
        .offset_mut(robot.y as isize)
786
        .as_mut() = 0i32;
786
        .as_mut() = 0i32;
787
}
787
}
788
/*Global variables. Bite me, it's fun.*/
788
/*Global variables. Bite me, it's fun.*/
789
#[no_mangle]
789
#[no_mangle]
790
pub static mut robot: screen_object = screen_object {
790
pub static mut robot: screen_object = screen_object {

797
#[no_mangle]
797
#[no_mangle]
798
pub unsafe extern "C" fn initialize_kitten() {
798
pub unsafe extern "C" fn initialize_kitten() {
799
    loop {
799
    loop {
800
        kitten.x = rand() % (COLS - 1i32) + 1i32;
800
        kitten.x = rand() % (COLS - 1i32) + 1i32;
801
        kitten.y = rand() % (LINES - 1i32 - 3i32 + 1i32) + 3i32;
801
        kitten.y = rand() % (LINES - 1i32 - 3i32 + 1i32) + 3i32;
802
        if !(*(*screen.offset(kitten.x as isize).as_mut())
802
        if !(*(*screen.offset_mut(kitten.x as isize).as_mut())
803
            .offset(kitten.y as isize)
803
            .offset_mut(kitten.y as isize)
804
            .as_mut()
804
            .as_mut()
805
            != -1i32)
805
            != -1i32)
806
        {
806
        {
807
            break;
807
            break;
808
        }
808
        }

811
        kitten.character = (rand() % (126i32 - '!' as i32 + 1i32) + '!' as i32) as libc::c_char;
811
        kitten.character = (rand() % (126i32 - '!' as i32 + 1i32) + '!' as i32) as libc::c_char;
812
        if !(0 == validchar(kitten.character)) {
812
        if !(0 == validchar(kitten.character)) {
813
            break;
813
            break;
814
        }
814
        }
815
    }
815
    }
816
    *(*screen.offset(kitten.x as isize).as_mut())
816
    *(*screen.offset_mut(kitten.x as isize).as_mut())
817
        .offset(kitten.y as isize)
817
        .offset_mut(kitten.y as isize)
818
        .as_mut() = 1i32;
818
        .as_mut() = 1i32;
819
    kitten.color = rand() % 6i32 + 1i32;
819
    kitten.color = rand() % 6i32 + 1i32;
820
    kitten.bold = 0 != if 0 != rand() % 2i32 { 1i32 } else { 0i32 };
820
    kitten.bold = 0 != if 0 != rand() % 2i32 { 1i32 } else { 0i32 };
821
}
821
}
822
#[no_mangle]
822
#[no_mangle]

852
            }
852
            }
853
        }
853
        }
854
        loop {
854
        loop {
855
            bogus[counter as usize].x = rand() % (COLS - 1i32) + 1i32;
855
            bogus[counter as usize].x = rand() % (COLS - 1i32) + 1i32;
856
            bogus[counter as usize].y = rand() % (LINES - 1i32 - 3i32 + 1i32) + 3i32;
856
            bogus[counter as usize].y = rand() % (LINES - 1i32 - 3i32 + 1i32) + 3i32;
857
            if !(*(*screen.offset(bogus[counter as usize].x as isize).as_mut())
857
            if !(*(*screen
858
                .offset(bogus[counter as usize].y as isize)
858
                .offset_mut(bogus[counter as usize].x as isize)
859
                .as_mut()
859
                .as_mut())
860
            .offset_mut(bogus[counter as usize].y as isize)
861
            .as_mut()
860
                != -1i32)
862
                != -1i32)
861
            {
863
            {
862
                break;
864
                break;
863
            }
865
            }
864
        }
866
        }
865
        *(*screen.offset(bogus[counter as usize].x as isize).as_mut())
867
        *(*screen
866
            .offset(bogus[counter as usize].y as isize)
868
            .offset_mut(bogus[counter as usize].x as isize)
869
            .as_mut())
870
        .offset_mut(bogus[counter as usize].y as isize)
867
            .as_mut() = counter + 2i32;
871
        .as_mut() = counter + 2i32;
868
        loop {
872
        loop {
869
            index = (rand() as libc::c_ulong).wrapping_rem(
873
            index = (rand() as libc::c_ulong).wrapping_rem(
870
                (::std::mem::size_of::<[*mut libc::c_char; 406]>() as libc::c_ulong)
874
                (::std::mem::size_of::<[*mut libc::c_char; 406]>() as libc::c_ulong)
871
                    .wrapping_div(::std::mem::size_of::<*mut libc::c_char>() as libc::c_ulong),
875
                    .wrapping_div(::std::mem::size_of::<*mut libc::c_char>() as libc::c_ulong),
872
            ) as libc::c_int;
876
            ) as libc::c_int;

1008
        if !(old_x == robot.x && old_y == robot.y) {
1012
        if !(old_x == robot.x && old_y == robot.y) {
1009
            if wmove(stdscr, old_y, old_x) == -1i32 {
1013
            if wmove(stdscr, old_y, old_x) == -1i32 {
1010
            } else {
1014
            } else {
1011
                waddch(stdscr, ' ' as i32 as chtype);
1015
                waddch(stdscr, ' ' as i32 as chtype);
1012
            };
1016
            };
1013
            *(*screen.offset(old_x as isize).as_mut())
1017
            *(*screen.offset_mut(old_x as isize).as_mut())
1014
                .offset(old_y as isize)
1018
                .offset_mut(old_y as isize)
1015
                .as_mut() = -1i32;
1019
                .as_mut() = -1i32;
1016
            draw(robot);
1020
            draw(robot);
1017
            wrefresh(stdscr);
1021
            wrefresh(stdscr);
1018
            *(*screen.offset(robot.x as isize).as_mut())
1022
            *(*screen.offset_mut(robot.x as isize).as_mut())
1019
                .offset(robot.y as isize)
1023
                .offset_mut(robot.y as isize)
1020
                .as_mut() = 0i32;
1024
                .as_mut() = 0i32;
1021
            old_x = robot.x;
1025
            old_x = robot.x;
1022
            old_y = robot.y
1026
            old_y = robot.y
1023
        }
1027
        }
1024
        input = wgetch(stdscr)
1028
        input = wgetch(stdscr)

1081
        }
1085
        }
1082
    }
1086
    }
1083
    if check_y < 3i32 || check_y > LINES - 1i32 || check_x < 0i32 || check_x > COLS - 1i32 {
1087
    if check_y < 3i32 || check_y > LINES - 1i32 || check_x < 0i32 || check_x > COLS - 1i32 {
1084
        return;
1088
        return;
1085
    }
1089
    }
1086
    if *(*screen.offset(check_x as isize).as_mut())
1090
    if *(*screen.offset_mut(check_x as isize).as_mut())
1087
        .offset(check_y as isize)
1091
        .offset_mut(check_y as isize)
1088
        .as_mut()
1092
        .as_mut()
1089
        != -1i32
1093
        != -1i32
1090
    {
1094
    {
1091
        match *(*screen.offset(check_x as isize).as_mut())
1095
        match *(*screen.offset_mut(check_x as isize).as_mut())
1092
            .offset(check_y as isize)
1096
            .offset_mut(check_y as isize)
1093
            .as_mut()
1097
            .as_mut()
1094
        {
1098
        {
1095
            0 => {}
1099
            0 => {}
1096
            1 => {
1100
            1 => {
1097
                /*We didn't move, or we're stuck in a
1101
                /*We didn't move, or we're stuck in a

1100
                wclrtoeol(stdscr);
1104
                wclrtoeol(stdscr);
1101
                play_animation(input);
1105
                play_animation(input);
1102
            }
1106
            }
1103
            _ => {
1107
            _ => {
1104
                message(
1108
                message(
1105
                    messages[bogus_messages[(*(*screen.offset(check_x as isize).as_mut())
1109
                    messages[bogus_messages[(*(*screen.offset_mut(check_x as isize).as_mut())
1106
                        .offset(check_y as isize)
1110
                        .offset_mut(check_y as isize)
1107
                        .as_mut()
1111
                        .as_mut()
1108
                        - 2i32) as usize] as usize]
1112
                        - 2i32) as usize] as usize]
1109
                        .as_ptr() as *mut i8,
1113
                        .as_ptr() as *mut i8,
1110
                );
1114
                );
1111
            }
1115
            }

This works fine for robotfindskitten, though in other codebases it may be necessary to properly distinguish mutable and immutable uses of offset.

With this change, the code typechecks with screens new memory-safe type, so we could stop here. However, unlike CBlockPtr, CArray supports array indexing - ptr[i] - in place of the convoluted *arr.offset(i).as_mut() syntax. So we perform a simple rewrite to make the code a little easier to read:

rewrite_expr
    'typed!(__e, ::c2rust_runtime::CArray<__t>).offset_mut(__f).as_mut()'
    '&mut __e[__f as usize]' ;
rewrite_expr '*&mut __e' '__e' ;

Diff #31

src/robotfindskitten.rs
713
                / ::std::mem::size_of::<::c2rust_runtime::CArray<i32>>(),
713
                / ::std::mem::size_of::<::c2rust_runtime::CArray<i32>>(),
714
        )
714
        )
715
    };
715
    };
716
    i = 0i32;
716
    i = 0i32;
717
    while i < COLS - 1i32 + 1i32 {
717
    while i < COLS - 1i32 + 1i32 {
718
        let ref mut fresh0 = *screen.offset_mut(i as isize).as_mut();
718
        let ref mut fresh0 = screen[i as isize as usize];
719
        *fresh0 = unsafe {
719
        *fresh0 = unsafe {
720
            ::c2rust_runtime::CArray::alloc(
720
            ::c2rust_runtime::CArray::alloc(
721
                (::std::mem::size_of::() as libc::c_ulong)
721
                (::std::mem::size_of::() as libc::c_ulong)
722
                    .wrapping_mul((LINES - 1i32 + 1i32) as libc::c_ulong) as usize
722
                    .wrapping_mul((LINES - 1i32 + 1i32) as libc::c_ulong) as usize
723
                    / ::std::mem::size_of::(),
723
                    / ::std::mem::size_of::(),

732
    empty.character = ' ' as i32 as libc::c_char;
732
    empty.character = ' ' as i32 as libc::c_char;
733
    counter = 0i32;
733
    counter = 0i32;
734
    while counter <= COLS - 1i32 {
734
    while counter <= COLS - 1i32 {
735
        counter2 = 0i32;
735
        counter2 = 0i32;
736
        while counter2 <= LINES - 1i32 {
736
        while counter2 <= LINES - 1i32 {
737
            *(*screen.offset_mut(counter as isize).as_mut())
737
            screen[counter as isize as usize][counter2 as isize as usize] = -1i32;
738
                .offset_mut(counter2 as isize)
739
                .as_mut() = -1i32;
740
            counter2 += 1
738
            counter2 += 1
741
        }
739
        }
742
        counter += 1
740
        counter += 1
743
    }
741
    }
744
    counter = 0i32;
742
    counter = 0i32;

779
    robot.x = rand() % (COLS - 1i32) + 1i32;
777
    robot.x = rand() % (COLS - 1i32) + 1i32;
780
    robot.y = rand() % (LINES - 1i32 - 3i32 + 1i32) + 3i32;
778
    robot.y = rand() % (LINES - 1i32 - 3i32 + 1i32) + 3i32;
781
    robot.character = '#' as i32 as libc::c_char;
779
    robot.character = '#' as i32 as libc::c_char;
782
    robot.color = 0i32;
780
    robot.color = 0i32;
783
    robot.bold = 0 != 0i32;
781
    robot.bold = 0 != 0i32;
784
    *(*screen.offset_mut(robot.x as isize).as_mut())
782
    screen[robot.x as isize as usize][robot.y as isize as usize] = 0i32;
785
        .offset_mut(robot.y as isize)
786
        .as_mut() = 0i32;
787
}
783
}
788
/*Global variables. Bite me, it's fun.*/
784
/*Global variables. Bite me, it's fun.*/
789
#[no_mangle]
785
#[no_mangle]
790
pub static mut robot: screen_object = screen_object {
786
pub static mut robot: screen_object = screen_object {
791
    x: 0,
787
    x: 0,

797
#[no_mangle]
793
#[no_mangle]
798
pub unsafe extern "C" fn initialize_kitten() {
794
pub unsafe extern "C" fn initialize_kitten() {
799
    loop {
795
    loop {
800
        kitten.x = rand() % (COLS - 1i32) + 1i32;
796
        kitten.x = rand() % (COLS - 1i32) + 1i32;
801
        kitten.y = rand() % (LINES - 1i32 - 3i32 + 1i32) + 3i32;
797
        kitten.y = rand() % (LINES - 1i32 - 3i32 + 1i32) + 3i32;
802
        if !(*(*screen.offset_mut(kitten.x as isize).as_mut())
798
        if !(screen[kitten.x as isize as usize][kitten.y as isize as usize] != -1i32) {
803
            .offset_mut(kitten.y as isize)
804
            .as_mut()
805
            != -1i32)
806
        {
807
            break;
799
            break;
808
        }
800
        }
809
    }
801
    }
810
    loop {
802
    loop {
811
        kitten.character = (rand() % (126i32 - '!' as i32 + 1i32) + '!' as i32) as libc::c_char;
803
        kitten.character = (rand() % (126i32 - '!' as i32 + 1i32) + '!' as i32) as libc::c_char;
812
        if !(0 == validchar(kitten.character)) {
804
        if !(0 == validchar(kitten.character)) {
813
            break;
805
            break;
814
        }
806
        }
815
    }
807
    }
816
    *(*screen.offset_mut(kitten.x as isize).as_mut())
808
    screen[kitten.x as isize as usize][kitten.y as isize as usize] = 1i32;
817
        .offset_mut(kitten.y as isize)
818
        .as_mut() = 1i32;
819
    kitten.color = rand() % 6i32 + 1i32;
809
    kitten.color = rand() % 6i32 + 1i32;
820
    kitten.bold = 0 != if 0 != rand() % 2i32 { 1i32 } else { 0i32 };
810
    kitten.bold = 0 != if 0 != rand() % 2i32 { 1i32 } else { 0i32 };
821
}
811
}
822
#[no_mangle]
812
#[no_mangle]
823
pub static mut kitten: screen_object = screen_object {
813
pub static mut kitten: screen_object = screen_object {

852
            }
842
            }
853
        }
843
        }
854
        loop {
844
        loop {
855
            bogus[counter as usize].x = rand() % (COLS - 1i32) + 1i32;
845
            bogus[counter as usize].x = rand() % (COLS - 1i32) + 1i32;
856
            bogus[counter as usize].y = rand() % (LINES - 1i32 - 3i32 + 1i32) + 3i32;
846
            bogus[counter as usize].y = rand() % (LINES - 1i32 - 3i32 + 1i32) + 3i32;
857
            if !(*(*screen
847
            if !(screen[bogus[counter as usize].x as isize as usize]
858
                .offset_mut(bogus[counter as usize].x as isize)
848
                [bogus[counter as usize].y as isize as usize]
859
                .as_mut())
860
            .offset_mut(bogus[counter as usize].y as isize)
861
            .as_mut()
862
                != -1i32)
849
                != -1i32)
863
            {
850
            {
864
                break;
851
                break;
865
            }
852
            }
866
        }
853
        }
867
        *(*screen
868
            .offset_mut(bogus[counter as usize].x as isize)
869
            .as_mut())
870
        .offset_mut(bogus[counter as usize].y as isize)
854
        screen[bogus[counter as usize].x as isize as usize]
871
        .as_mut() = counter + 2i32;
855
            [bogus[counter as usize].y as isize as usize] = counter + 2i32;
872
        loop {
856
        loop {
873
            index = (rand() as libc::c_ulong).wrapping_rem(
857
            index = (rand() as libc::c_ulong).wrapping_rem(
874
                (::std::mem::size_of::<[*mut libc::c_char; 406]>() as libc::c_ulong)
858
                (::std::mem::size_of::<[*mut libc::c_char; 406]>() as libc::c_ulong)
875
                    .wrapping_div(::std::mem::size_of::<*mut libc::c_char>() as libc::c_ulong),
859
                    .wrapping_div(::std::mem::size_of::<*mut libc::c_char>() as libc::c_ulong),
876
            ) as libc::c_int;
860
            ) as libc::c_int;

1012
        if !(old_x == robot.x && old_y == robot.y) {
996
        if !(old_x == robot.x && old_y == robot.y) {
1013
            if wmove(stdscr, old_y, old_x) == -1i32 {
997
            if wmove(stdscr, old_y, old_x) == -1i32 {
1014
            } else {
998
            } else {
1015
                waddch(stdscr, ' ' as i32 as chtype);
999
                waddch(stdscr, ' ' as i32 as chtype);
1016
            };
1000
            };
1017
            *(*screen.offset_mut(old_x as isize).as_mut())
1001
            screen[old_x as isize as usize][old_y as isize as usize] = -1i32;
1018
                .offset_mut(old_y as isize)
1019
                .as_mut() = -1i32;
1020
            draw(robot);
1002
            draw(robot);
1021
            wrefresh(stdscr);
1003
            wrefresh(stdscr);
1022
            *(*screen.offset_mut(robot.x as isize).as_mut())
1004
            screen[robot.x as isize as usize][robot.y as isize as usize] = 0i32;
1023
                .offset_mut(robot.y as isize)
1024
                .as_mut() = 0i32;
1025
            old_x = robot.x;
1005
            old_x = robot.x;
1026
            old_y = robot.y
1006
            old_y = robot.y
1027
        }
1007
        }
1028
        input = wgetch(stdscr)
1008
        input = wgetch(stdscr)
1029
    }
1009
    }

1085
        }
1065
        }
1086
    }
1066
    }
1087
    if check_y < 3i32 || check_y > LINES - 1i32 || check_x < 0i32 || check_x > COLS - 1i32 {
1067
    if check_y < 3i32 || check_y > LINES - 1i32 || check_x < 0i32 || check_x > COLS - 1i32 {
1088
        return;
1068
        return;
1089
    }
1069
    }
1090
    if *(*screen.offset_mut(check_x as isize).as_mut())
1070
    if screen[check_x as isize as usize][check_y as isize as usize] != -1i32 {
1091
        .offset_mut(check_y as isize)
1071
        match screen[check_x as isize as usize][check_y as isize as usize] {
1092
        .as_mut()
1093
        != -1i32
1094
    {
1095
        match *(*screen.offset_mut(check_x as isize).as_mut())
1096
            .offset_mut(check_y as isize)
1097
            .as_mut()
1098
        {
1099
            0 => {}
1072
            0 => {}
1100
            1 => {
1073
            1 => {
1101
                /*We didn't move, or we're stuck in a
1074
                /*We didn't move, or we're stuck in a
1102
                time warp or something.*/
1075
                time warp or something.*/
1103
                wmove(stdscr, 1i32, 0i32);
1076
                wmove(stdscr, 1i32, 0i32);
1104
                wclrtoeol(stdscr);
1077
                wclrtoeol(stdscr);
1105
                play_animation(input);
1078
                play_animation(input);
1106
            }
1079
            }
1107
            _ => {
1080
            _ => {
1108
                message(
1081
                message(
1109
                    messages[bogus_messages[(*(*screen.offset_mut(check_x as isize).as_mut())
1082
                    messages[bogus_messages[(screen[check_x as isize as usize]
1110
                        .offset_mut(check_y as isize)
1083
                        [check_y as isize as usize]
1111
                        .as_mut()
1112
                        - 2i32) as usize] as usize]
1084
                        - 2i32) as usize] as usize]
1113
                        .as_ptr() as *mut i8,
1085
                        .as_ptr() as *mut i8,
1114
                );
1086
                );
1115
            }
1087
            }
1116
        }
1088
        }

Finally, we remove unsafety from screen's static initializer. It currently calls CArray::from_ptr(0 as *mut _), which is unsafe because CArray::from_ptr requires its pointer argument to must satisfy certain properties. But CArray also provides a safe method specifically for initializing a CArray to null, which we can use instead:

rewrite_expr
    '::c2rust_runtime::CArray::from_ptr(cast!(0))'
    '::c2rust_runtime::CArray::empty()' ;

Diff #32

src/robotfindskitten.rs
765
/* This array contains our internal representation of the screen. The
765
/* This array contains our internal representation of the screen. The
766
array is bigger than it needs to be, as we don't need to keep track
766
array is bigger than it needs to be, as we don't need to keep track
767
of the first few rows of the screen. But that requires making an
767
of the first few rows of the screen. But that requires making an
768
offset function and using that everywhere. So not right now. */
768
offset function and using that everywhere. So not right now. */
769
#[no_mangle]
769
#[no_mangle]
770
pub static mut screen: ::c2rust_runtime::CArray<::c2rust_runtime::CArray> = unsafe {
770
pub static mut screen: ::c2rust_runtime::CArray<::c2rust_runtime::CArray> =
771
    ::c2rust_runtime::CArray::from_ptr(
771
    unsafe { ::c2rust_runtime::CArray::empty() };
772
        0 as *const *mut libc::c_int as *mut *mut libc::c_int as *mut ::c2rust_runtime::CArray<i32>,
773
    )
774
};
775
#[no_mangle]
772
#[no_mangle]
776
pub unsafe extern "C" fn initialize_robot() {
773
pub unsafe extern "C" fn initialize_robot() {
777
    robot.x = rand() % (COLS - 1i32) + 1i32;
774
    robot.x = rand() % (COLS - 1i32) + 1i32;
778
    robot.y = rand() % (LINES - 1i32 - 3i32 + 1i32) + 3i32;
775
    robot.y = rand() % (LINES - 1i32 - 3i32 + 1i32) + 3i32;
779
    robot.character = '#' as i32 as libc::c_char;
776
    robot.character = '#' as i32 as libc::c_char;

This completes the refactoring of screen, as all raw pointer manipulations have been replaced with safe CArray method calls. The only remaining unsafety arises from the fact that screen is a static mut, which we address in a later refactoring step.

commit ;

Using the pancurses library

The pancurses library provides safe wrappers around ncurses APIs. Since the pancurses and ncurses APIs are so similar, we can automatically convert the unsafe ncurses FFI calls in robotfindskitten to safe pancurses calls, avoiding the need to maintain safe wrappers in robotfindskitten itself.

There are two preliminary steps before we do the actual conversion. First, we must import the pancurses library:

select target 'crate;' ;
create_item 'extern crate pancurses;' inside ;

Diff #33

src/robotfindskitten.rs
7
    unused_mut
7
    unused_mut
8
)]
8
)]
9
#![feature(const_raw_ptr_to_usize_cast, extern_types, libc)]
9
#![feature(const_raw_ptr_to_usize_cast, extern_types, libc)]
10
extern crate c2rust_runtime;
10
extern crate c2rust_runtime;
11
extern crate libc;
11
extern crate libc;
12
extern crate pancurses;
12
extern "C" {
13
extern "C" {
13
    pub type ldat;
14
    pub type ldat;
14
    #[no_mangle]
15
    #[no_mangle]
15
    fn printf(_: *const libc::c_char, ...) -> libc::c_int;
16
    fn printf(_: *const libc::c_char, ...) -> libc::c_int;
16
    #[no_mangle]
17
    #[no_mangle]

And second, we must create a global variable to store the main pancurses Window:

select target 'crate;' ;
create_item 'static mut win: Option<::pancurses::Window> = None;' inside ;

Diff #34

src/robotfindskitten.rs
5
    non_snake_case,
5
    non_snake_case,
6
    non_upper_case_globals,
6
    non_upper_case_globals,
7
    unused_mut
7
    unused_mut
8
)]
8
)]
9
#![feature(const_raw_ptr_to_usize_cast, extern_types, libc)]
9
#![feature(const_raw_ptr_to_usize_cast, extern_types, libc)]
10
static mut win: Option<::pancurses::Window> = None;
10
extern crate c2rust_runtime;
11
extern crate c2rust_runtime;
11
extern crate libc;
12
extern crate libc;
12
extern crate pancurses;
13
extern crate pancurses;
13
extern "C" {
14
extern "C" {
14
    pub type ldat;
15
    pub type ldat;

pancurses doesn't have an equivalent of the global stdscr window that ncurses provides. Instead, the pancurses initialization function creates an initial Window object that must be passed around to each function that updates the display. We store that initial Window in the global win variable so that it's accessible everywhere that stdscr is used.

Note that making win a static mut makes it unsafe to access. However, a later refactoring pass will gather up all static muts, including win, and collect them into a stack-allocated struct, at which point accessing win will no longer be unsafe.

General library calls

We convert ncurses library calls to pancurses ones in a few stages.

First, for functions that don't require a window object, we simply replace each ncurses function with its equivalent in the pancurses library:

rewrite_expr 'nonl' '::pancurses::nonl' ;
rewrite_expr 'noecho' '::pancurses::noecho' ;
rewrite_expr 'cbreak' '::pancurses::cbreak' ;
rewrite_expr 'has_colors' '::pancurses::has_colors' ;
rewrite_expr 'start_color' '::pancurses::start_color' ;
rewrite_expr 'endwin' '::pancurses::endwin' ;
rewrite_expr 'init_pair' '::pancurses::init_pair' ;

Diff #35

src/robotfindskitten.rs
638
#[no_mangle]
638
#[no_mangle]
639
pub unsafe extern "C" fn initialize_ncurses() {
639
pub unsafe extern "C" fn initialize_ncurses() {
640
    signal(2i32, Some(finish));
640
    signal(2i32, Some(finish));
641
    initscr();
641
    initscr();
642
    keypad(stdscr, 0 != 1i32);
642
    keypad(stdscr, 0 != 1i32);
643
    nonl();
643
    ::pancurses::nonl();
644
    intrflush(stdscr, 0 != 0i32);
644
    intrflush(stdscr, 0 != 0i32);
645
    noecho();
645
    ::pancurses::noecho();
646
    cbreak();
646
    ::pancurses::cbreak();
647
    if has_colors() {
647
    if ::pancurses::has_colors() {
648
        start_color();
648
        ::pancurses::start_color();
649
        init_pair(
649
        ::pancurses::init_pair(
650
            0i32 as libc::c_short,
650
            0i32 as libc::c_short,
651
            0i32 as libc::c_short,
651
            0i32 as libc::c_short,
652
            0i32 as libc::c_short,
652
            0i32 as libc::c_short,
653
        );
653
        );
654
        init_pair(
654
        ::pancurses::init_pair(
655
            2i32 as libc::c_short,
655
            2i32 as libc::c_short,
656
            2i32 as libc::c_short,
656
            2i32 as libc::c_short,
657
            0i32 as libc::c_short,
657
            0i32 as libc::c_short,
658
        );
658
        );
659
        init_pair(
659
        ::pancurses::init_pair(
660
            1i32 as libc::c_short,
660
            1i32 as libc::c_short,
661
            1i32 as libc::c_short,
661
            1i32 as libc::c_short,
662
            0i32 as libc::c_short,
662
            0i32 as libc::c_short,
663
        );
663
        );
664
        init_pair(
664
        ::pancurses::init_pair(
665
            6i32 as libc::c_short,
665
            6i32 as libc::c_short,
666
            6i32 as libc::c_short,
666
            6i32 as libc::c_short,
667
            0i32 as libc::c_short,
667
            0i32 as libc::c_short,
668
        );
668
        );
669
        init_pair(
669
        ::pancurses::init_pair(
670
            7i32 as libc::c_short,
670
            7i32 as libc::c_short,
671
            7i32 as libc::c_short,
671
            7i32 as libc::c_short,
672
            0i32 as libc::c_short,
672
            0i32 as libc::c_short,
673
        );
673
        );
674
        init_pair(
674
        ::pancurses::init_pair(
675
            5i32 as libc::c_short,
675
            5i32 as libc::c_short,
676
            5i32 as libc::c_short,
676
            5i32 as libc::c_short,
677
            0i32 as libc::c_short,
677
            0i32 as libc::c_short,
678
        );
678
        );
679
        init_pair(
679
        ::pancurses::init_pair(
680
            4i32 as libc::c_short,
680
            4i32 as libc::c_short,
681
            4i32 as libc::c_short,
681
            4i32 as libc::c_short,
682
            0i32 as libc::c_short,
682
            0i32 as libc::c_short,
683
        );
683
        );
684
        init_pair(
684
        ::pancurses::init_pair(
685
            3i32 as libc::c_short,
685
            3i32 as libc::c_short,
686
            3i32 as libc::c_short,
686
            3i32 as libc::c_short,
687
            0i32 as libc::c_short,
687
            0i32 as libc::c_short,
688
        );
688
        );
689
    };
689
    };
690
}
690
}
691
unsafe extern "C" fn finish(mut sig: libc::c_int) {
691
unsafe extern "C" fn finish(mut sig: libc::c_int) {
692
    endwin();
692
    ::pancurses::endwin();
693
    fmt_printf(format_args!(
693
    fmt_printf(format_args!(
694
        "{:}{:}{:}",
694
        "{:}{:}{:}",
695
        27i32 as u8 as char, '(' as i32 as u8 as char, 'B' as i32 as u8 as char
695
        27i32 as u8 as char, '(' as i32 as u8 as char, 'B' as i32 as u8 as char
696
    ));
696
    ));
697
    exit(0i32);
697
    exit(0i32);

Next, functions taking a window are replaced with method calls on the static win variable we defined earlier:

rewrite_expr 'wrefresh(stdscr)' 'win.refresh()' ;
rewrite_expr 'wrefresh(curscr)' 'win.refresh()' ;
rewrite_expr 'keypad(stdscr, __bf)' 'win.keypad(__bf)' ;
rewrite_expr 'wmove(stdscr, __my, __mx)' 'win.mv(__my, __mx)' ;
rewrite_expr 'wclear(stdscr)' 'win.clear()' ;
rewrite_expr 'wclrtoeol(stdscr)' 'win.clrtoeol()' ;
rewrite_expr 'waddch(stdscr, __ch)' 'win.addch(__ch)' ;

rewrite_expr
    'wattr_get(stdscr, __attrs, __pair, __e)'
    '{
        let tmp = win.attrget();
        *__attrs = tmp.0;
        *__pair = tmp.1;
        0
    }' ;
rewrite_expr
    'wattrset(stdscr, __attrs)'
    'win.attrset(__attrs as ::pancurses::chtype)' ;

Diff #36

src/robotfindskitten.rs
637
/*Initialization and setup functions*/
637
/*Initialization and setup functions*/
638
#[no_mangle]
638
#[no_mangle]
639
pub unsafe extern "C" fn initialize_ncurses() {
639
pub unsafe extern "C" fn initialize_ncurses() {
640
    signal(2i32, Some(finish));
640
    signal(2i32, Some(finish));
641
    initscr();
641
    initscr();
642
    keypad(stdscr, 0 != 1i32);
642
    win.keypad(0 != 1i32);
643
    ::pancurses::nonl();
643
    ::pancurses::nonl();
644
    intrflush(stdscr, 0 != 0i32);
644
    intrflush(stdscr, 0 != 0i32);
645
    ::pancurses::noecho();
645
    ::pancurses::noecho();
646
    ::pancurses::cbreak();
646
    ::pancurses::cbreak();
647
    if ::pancurses::has_colors() {
647
    if ::pancurses::has_colors() {

890
       draw(bogus[counter as usize]);
890
       draw(bogus[counter as usize]);
891
        counter += 1
891
        counter += 1
892
    }
892
    }
893
    draw(kitten);
893
    draw(kitten);
894
    draw(robot);
894
    draw(robot);
895
    wrefresh(stdscr);
895
    win.refresh();
896
}
896
}
897
#[no_mangle]
897
#[no_mangle]
898
pub unsafe extern "C" fn draw(mut o: screen_object) {
898
pub unsafe extern "C" fn draw(mut o: screen_object) {
899
    full_draw(o, 0 != 0i32);
899
    full_draw(o, 0 != 0i32);
900
}
900
}

923
       new |= 1u64 << 14i32 + 8i32
923
       new |= 1u64 << 14i32 + 8i32
924
    }
924
    }
925
    if o.bold {
925
    if o.bold {
926
        new |= 1u64 << 13i32 + 8i32
926
        new |= 1u64 << 13i32 + 8i32
927
    }
927
    }
928
    wattrset(stdscr, new as libc::c_int);
928
    win.attrset(new as libc::c_int as ::pancurses::chtype);
929
    if in_place {
929
    if in_place {
930
        fmt_printw(format_args!(
930
        fmt_printw(format_args!(
931
            "{:}",
931
            "{:}",
932
            o.character as libc::c_int as u8 as char
932
            o.character as libc::c_int as u8 as char
933
        ));
933
        ));

935
       fmt_mvprintw(
935
       fmt_mvprintw(
936
            o.y,
936
            o.y,
937
            o.x,
937
            o.x,
938
            format_args!("{:}", o.character as libc::c_int as u8 as char),
938
            format_args!("{:}", o.character as libc::c_int as u8 as char),
939
        );
939
        );
940
        wmove(stdscr, o.y, o.x);
940
        win.mv(o.y, o.x);
941
    }
941
    }
942
    wattrset(stdscr, old as libc::c_int);
942
    win.attrset(old as libc::c_int as ::pancurses::chtype);
943
}
943
}
944
#[no_mangle]
944
#[no_mangle]
945
pub unsafe extern "C" fn instructions() {
945
pub unsafe extern "C" fn instructions() {
946
    let mut dummy: libc::c_char = 0;
946
    let mut dummy: libc::c_char = 0;
947
    fmt_mvprintw(
947
    fmt_mvprintw(

973
   ));
973
   ));
974
    fmt_printw(format_args!(
974
    fmt_printw(format_args!(
975
        "the Esc key. See the documentation for more information.\n\n"
975
        "the Esc key. See the documentation for more information.\n\n"
976
    ));
976
    ));
977
    fmt_printw(format_args!("Press any key to start.\n"));
977
    fmt_printw(format_args!("Press any key to start.\n"));
978
    wrefresh(stdscr);
978
    win.refresh();
979
    dummy = wgetch(stdscr) as libc::c_char;
979
    dummy = wgetch(stdscr) as libc::c_char;
980
    wclear(stdscr);
980
    win.clear();
981
}
981
}
982
#[no_mangle]
982
#[no_mangle]
983
pub unsafe extern "C" fn draw_in_place(mut o: screen_object) {
983
pub unsafe extern "C" fn draw_in_place(mut o: screen_object) {
984
    full_draw(o, 0 != 1i32);
984
    full_draw(o, 0 != 1i32);
985
}
985
}

991
   let mut input: libc::c_int = 0;
991
   let mut input: libc::c_int = 0;
992
    input = wgetch(stdscr);
992
    input = wgetch(stdscr);
993
    while input != 27i32 && input != 'q' as i32 && input != 'Q' as i32 {
993
    while input != 27i32 && input != 'q' as i32 && input != 'Q' as i32 {
994
        process_input(input);
994
        process_input(input);
995
        if !(old_x == robot.x && old_y == robot.y) {
995
        if !(old_x == robot.x && old_y == robot.y) {
996
            if wmove(stdscr, old_y, old_x) == -1i32 {
996
            if win.mv(old_y, old_x) == -1i32 {
997
            } else {
997
            } else {
998
                waddch(stdscr, ' ' as i32 as chtype);
998
                win.addch(' ' as i32 as chtype);
999
            };
999
            };
1000
            screen[old_x as isize as usize][old_y as isize as usize] = -1i32;
1000
            screen[old_x as isize as usize][old_y as isize as usize] = -1i32;
1001
            draw(robot);
1001
            draw(robot);
1002
            wrefresh(stdscr);
1002
            win.refresh();
1003
            screen[robot.x as isize as usize][robot.y as isize as usize] = 0i32;
1003
            screen[robot.x as isize as usize][robot.y as isize as usize] = 0i32;
1004
            old_x = robot.x;
1004
            old_x = robot.x;
1005
            old_y = robot.y
1005
            old_y = robot.y
1006
        }
1006
        }
1007
        input = wgetch(stdscr)
1007
        input = wgetch(stdscr)
1008
    }
1008
    }
1009
    message(b"Bye!\x00" as *const u8 as *const libc::c_char as *mut libc::c_char);
1009
    message(b"Bye!\x00" as *const u8 as *const libc::c_char as *mut libc::c_char);
1010
    wrefresh(stdscr);
1010
    win.refresh();
1011
    finish(0i32);
1011
    finish(0i32);
1012
}
1012
}
1013
#[no_mangle]
1013
#[no_mangle]
1014
pub unsafe extern "C" fn message(mut message_0: *mut libc::c_char) {
1014
pub unsafe extern "C" fn message(mut message_0: *mut libc::c_char) {
1015
    wmove(stdscr, 1i32, 0i32);
1015
    win.mv(1i32, 0i32);
1016
    wclrtoeol(stdscr);
1016
    win.clrtoeol();
1017
    fmt_mvprintw(
1017
    fmt_mvprintw(
1018
        1i32,
1018
        1i32,
1019
        0i32,
1019
        0i32,
1020
        format_args!("{:.*}", COLS as usize, unsafe {
1020
        format_args!("{:.*}", COLS as usize, unsafe {
1021
            ::std::ffi::CStr::from_ptr(message_0 as *const libc::c_char)
1021
            ::std::ffi::CStr::from_ptr(message_0 as *const libc::c_char)
1022
                .to_str()
1022
                .to_str()
1023
                .unwrap()
1023
                .unwrap()
1024
        }),
1024
        }),
1025
    );
1025
    );
1026
    wmove(stdscr, robot.y, robot.x);
1026
    win.mv(robot.y, robot.x);
1027
    wrefresh(stdscr);
1027
    win.refresh();
1028
}
1028
}
1029
#[no_mangle]
1029
#[no_mangle]
1030
pub unsafe extern "C" fn process_input(mut input: libc::c_int) {
1030
pub unsafe extern "C" fn process_input(mut input: libc::c_int) {
1031
    let mut check_x: libc::c_int = robot.x;
1031
    let mut check_x: libc::c_int = robot.x;
1032
    let mut check_y: libc::c_int = robot.y;
1032
    let mut check_y: libc::c_int = robot.y;
1033
    match input {
1033
    match input {
1034
        12 => {
1034
        12 => {
1035
            wrefresh(curscr);
1035
            win.refresh();
1036
        }
1036
        }
1037
        259 | 107 | 75 | 16 => check_y -= 1,
1037
        259 | 107 | 75 | 16 => check_y -= 1,
1038
        262 | 121 | 89 => {
1038
        262 | 121 | 89 => {
1039
            check_x -= 1;
1039
            check_x -= 1;
1040
            check_y -= 1
1040
            check_y -= 1

1070
       match screen[check_x as isize as usize][check_y as isize as usize] {
1070
       match screen[check_x as isize as usize][check_y as isize as usize] {
1071
            0 => {}
1071
            0 => {}
1072
            1 => {
1072
            1 => {
1073
                /*We didn't move, or we're stuck in a
1073
                /*We didn't move, or we're stuck in a
1074
                time warp or something.*/
1074
                time warp or something.*/
1075
                wmove(stdscr, 1i32, 0i32);
1075
                win.mv(1i32, 0i32);
1076
                wclrtoeol(stdscr);
1076
                win.clrtoeol();
1077
                play_animation(input);
1077
                play_animation(input);
1078
            }
1078
            }
1079
            _ => {
1079
            _ => {
1080
                message(
1080
                message(
1081
                    messages[bogus_messages[(screen[check_x as isize as usize]
1081
                    messages[bogus_messages[(screen[check_x as isize as usize]

1093
#[no_mangle]
1093
#[no_mangle]
1094
pub unsafe extern "C" fn play_animation(mut input: libc::c_int) {
1094
pub unsafe extern "C" fn play_animation(mut input: libc::c_int) {
1095
    let mut counter: libc::c_int = 0;
1095
    let mut counter: libc::c_int = 0;
1096
    counter = 4i32;
1096
    counter = 4i32;
1097
    while counter > 0i32 {
1097
    while counter > 0i32 {
1098
        if wmove(stdscr, 1i32, 50i32 + counter + 1i32) == -1i32 {
1098
        if win.mv(1i32, 50i32 + counter + 1i32) == -1i32 {
1099
        } else {
1099
        } else {
1100
            waddch(stdscr, ' ' as i32 as chtype);
1100
            win.addch(' ' as i32 as chtype);
1101
        };
1101
        };
1102
        wmove(stdscr, 1i32, 50i32 + counter);
1102
        win.mv(1i32, 50i32 + counter);
1103
        if input == 0o405i32 || input == 0o402i32 || input == 0o540i32 || input == 0o535i32 {
1103
        if input == 0o405i32 || input == 0o402i32 || input == 0o540i32 || input == 0o535i32 {
1104
            draw_in_place(kitten);
1104
            draw_in_place(kitten);
1105
        } else {
1105
        } else {
1106
            draw_in_place(robot);
1106
            draw_in_place(robot);
1107
        }
1107
        }
1108
        if wmove(stdscr, 1i32, 50i32 - counter) == -1i32 {
1108
        if win.mv(1i32, 50i32 - counter) == -1i32 {
1109
        } else {
1109
        } else {
1110
            waddch(stdscr, ' ' as i32 as chtype);
1110
            win.addch(' ' as i32 as chtype);
1111
        };
1111
        };
1112
        wmove(stdscr, 1i32, 50i32 - counter + 1i32);
1112
        win.mv(1i32, 50i32 - counter + 1i32);
1113
        if input == 0o405i32 || input == 0o402i32 || input == 0o540i32 || input == 0o535i32 {
1113
        if input == 0o405i32 || input == 0o402i32 || input == 0o540i32 || input == 0o535i32 {
1114
            draw_in_place(robot);
1114
            draw_in_place(robot);
1115
        } else {
1115
        } else {
1116
            draw_in_place(kitten);
1116
            draw_in_place(kitten);
1117
        }
1117
        }
1118
        wrefresh(stdscr);
1118
        win.refresh();
1119
        sleep(1i32 as libc::c_uint);
1119
        sleep(1i32 as libc::c_uint);
1120
        counter -= 1
1120
        counter -= 1
1121
    }
1121
    }
1122
    wmove(stdscr, 1i32, 0i32);
1122
    win.mv(1i32, 0i32);
1123
    waddnstr(
1123
    waddnstr(
1124
        stdscr,
1124
        stdscr,
1125
        b"You found kitten! Way to go, robot!\x00" as *const u8 as *const libc::c_char,
1125
        b"You found kitten! Way to go, robot!\x00" as *const u8 as *const libc::c_char,
1126
        -1i32,
1126
        -1i32,
1127
    );
1127
    );
1128
    wrefresh(stdscr);
1128
    win.refresh();
1129
    finish(0i32);
1129
    finish(0i32);
1130
}
1130
}
1131
unsafe fn main_0(mut argc: libc::c_int, mut argv: *mut *mut libc::c_char) -> libc::c_int {
1131
unsafe fn main_0(mut argc: libc::c_int, mut argv: *mut *mut libc::c_char) -> libc::c_int {
1132
    if argc == 1i32 {
1132
    if argc == 1i32 {
1133
        num_bogus = 20i32
1133
        num_bogus = 20i32

For simplicity, we write win.f(...) in the rewrite_expr replacement arguments, even though win is actually an Option<Window>, not a Window. Later, we replace win with win.as_ref().unwrap() throughout the crate to correct the resulting type errors.

We next replace some ncurses global variables with calls to corresponding pancurses functions:

rewrite_expr 'LINES' 'win.get_max_y()' ;
rewrite_expr 'COLS' 'win.get_max_x()' ;

Diff #37

src/robotfindskitten.rs
709
   };
709
   };
710
    let mut i: libc::c_int = 0i32;
710
    let mut i: libc::c_int = 0i32;
711
    screen = unsafe {
711
    screen = unsafe {
712
        ::c2rust_runtime::CArray::alloc(
712
        ::c2rust_runtime::CArray::alloc(
713
            (::std::mem::size_of::<*mut libc::c_int>() as libc::c_ulong)
713
            (::std::mem::size_of::<*mut libc::c_int>() as libc::c_ulong)
714
                .wrapping_mul((COLS - 1i32 + 1i32) as libc::c_ulong) as usize
714
                .wrapping_mul((win.get_max_x() - 1i32 + 1i32) as libc::c_ulong)
715
                as usize
715
                / ::std::mem::size_of::<::c2rust_runtime::CArray<i32>>(),
716
                / ::std::mem::size_of::<::c2rust_runtime::CArray<i32>>(),
716
        )
717
        )
717
    };
718
    };
718
    i = 0i32;
719
    i = 0i32;
719
    while i < COLS - 1i32 + 1i32 {
720
    while i < win.get_max_x() - 1i32 + 1i32 {
720
        let ref mut fresh0 = screen[i as isize as usize];
721
        let ref mut fresh0 = screen[i as isize as usize];
721
        *fresh0 = unsafe {
722
        *fresh0 = unsafe {
722
            ::c2rust_runtime::CArray::alloc(
723
            ::c2rust_runtime::CArray::alloc(
723
                (::std::mem::size_of::() as libc::c_ulong)
724
                (::std::mem::size_of::() as libc::c_ulong)
724
                    .wrapping_mul((LINES - 1i32 + 1i32) as libc::c_ulong) as usize
725
                    .wrapping_mul((win.get_max_y() - 1i32 + 1i32) as libc::c_ulong)
726
                    as usize
725
                    / ::std::mem::size_of::(),
727
                    / ::std::mem::size_of::(),
726
            )
728
            )
727
        };
729
        };
728
        i += 1
730
        i += 1
729
    }
731
    }

731
   empty.y = -1i32;
733
   empty.y = -1i32;
732
    empty.color = 0i32;
734
    empty.color = 0i32;
733
    empty.bold = 0 != 0i32;
735
    empty.bold = 0 != 0i32;
734
    empty.character = ' ' as i32 as libc::c_char;
736
    empty.character = ' ' as i32 as libc::c_char;
735
    counter = 0i32;
737
    counter = 0i32;
736
    while counter <= COLS - 1i32 {
738
    while counter <= win.get_max_x() - 1i32 {
737
        counter2 = 0i32;
739
        counter2 = 0i32;
738
        while counter2 <= LINES - 1i32 {
740
        while counter2 <= win.get_max_y() - 1i32 {
739
            screen[counter as isize as usize][counter2 as isize as usize] = -1i32;
741
            screen[counter as isize as usize][counter2 as isize as usize] = -1i32;
740
            counter2 += 1
742
            counter2 += 1
741
        }
743
        }
742
        counter += 1
744
        counter += 1
743
    }
745
    }

771
#[no_mangle]
773
#[no_mangle]
772
pub static mut screen: ::c2rust_runtime::CArray<::c2rust_runtime::CArray> =
774
pub static mut screen: ::c2rust_runtime::CArray<::c2rust_runtime::CArray> =
773
    unsafe { ::c2rust_runtime::CArray::empty() };
775
    unsafe { ::c2rust_runtime::CArray::empty() };
774
#[no_mangle]
776
#[no_mangle]
775
pub unsafe extern "C" fn initialize_robot() {
777
pub unsafe extern "C" fn initialize_robot() {
776
    robot.x = rand() % (COLS - 1i32) + 1i32;
778
    robot.x = rand() % (win.get_max_x() - 1i32) + 1i32;
777
    robot.y = rand() % (LINES - 1i32 - 3i32 + 1i32) + 3i32;
779
    robot.y = rand() % (win.get_max_y() - 1i32 - 3i32 + 1i32) + 3i32;
778
    robot.character = '#' as i32 as libc::c_char;
780
    robot.character = '#' as i32 as libc::c_char;
779
    robot.color = 0i32;
781
    robot.color = 0i32;
780
    robot.bold = 0 != 0i32;
782
    robot.bold = 0 != 0i32;
781
    screen[robot.x as isize as usize][robot.y as isize as usize] = 0i32;
783
    screen[robot.x as isize as usize][robot.y as isize as usize] = 0i32;
782
}
784
}

790
   character: 0,
792
   character: 0,
791
};
793
};
792
#[no_mangle]
794
#[no_mangle]
793
pub unsafe extern "C" fn initialize_kitten() {
795
pub unsafe extern "C" fn initialize_kitten() {
794
    loop {
796
    loop {
795
        kitten.x = rand() % (COLS - 1i32) + 1i32;
797
        kitten.x = rand() % (win.get_max_x() - 1i32) + 1i32;
796
        kitten.y = rand() % (LINES - 1i32 - 3i32 + 1i32) + 3i32;
798
        kitten.y = rand() % (win.get_max_y() - 1i32 - 3i32 + 1i32) + 3i32;
797
        if !(screen[kitten.x as isize as usize][kitten.y as isize as usize] != -1i32) {
799
        if !(screen[kitten.x as isize as usize][kitten.y as isize as usize] != -1i32) {
798
            break;
800
            break;
799
        }
801
        }
800
    }
802
    }
801
    loop {
803
    loop {

839
           if !(0 == validchar(bogus[counter as usize].character)) {
841
           if !(0 == validchar(bogus[counter as usize].character)) {
840
                break;
842
                break;
841
            }
843
            }
842
        }
844
        }
843
        loop {
845
        loop {
844
            bogus[counter as usize].x = rand() % (COLS - 1i32) + 1i32;
846
            bogus[counter as usize].x = rand() % (win.get_max_x() - 1i32) + 1i32;
845
            bogus[counter as usize].y = rand() % (LINES - 1i32 - 3i32 + 1i32) + 3i32;
847
            bogus[counter as usize].y = rand() % (win.get_max_y() - 1i32 - 3i32 + 1i32) + 3i32;
846
            if !(screen[bogus[counter as usize].x as isize as usize]
848
            if !(screen[bogus[counter as usize].x as isize as usize]
847
                [bogus[counter as usize].y as isize as usize]
849
                [bogus[counter as usize].y as isize as usize]
848
                != -1i32)
850
                != -1i32)
849
            {
851
            {
850
                break;
852
                break;

879
               .to_str()
881
               .to_str()
880
                .unwrap()
882
                .unwrap()
881
        }),
883
        }),
882
    );
884
    );
883
    counter = 0i32;
885
    counter = 0i32;
884
    while counter <= COLS - 1i32 {
886
    while counter <= win.get_max_x() - 1i32 {
885
        fmt_printw(format_args!("{:}", 95i32 as u8 as char));
887
        fmt_printw(format_args!("{:}", 95i32 as u8 as char));
886
        counter += 1
888
        counter += 1
887
    }
889
    }
888
    counter = 0i32;
890
    counter = 0i32;
889
    while counter < num_bogus {
891
    while counter < num_bogus {

1015
   win.mv(1i32, 0i32);
1017
   win.mv(1i32, 0i32);
1016
    win.clrtoeol();
1018
    win.clrtoeol();
1017
    fmt_mvprintw(
1019
    fmt_mvprintw(
1018
        1i32,
1020
        1i32,
1019
        0i32,
1021
        0i32,
1020
        format_args!("{:.*}", COLS as usize, unsafe {
1022
        format_args!("{:.*}", win.get_max_x() as usize, unsafe {
1021
            ::std::ffi::CStr::from_ptr(message_0 as *const libc::c_char)
1023
            ::std::ffi::CStr::from_ptr(message_0 as *const libc::c_char)
1022
                .to_str()
1024
                .to_str()
1023
                .unwrap()
1025
                .unwrap()
1024
        }),
1026
        }),
1025
    );
1027
    );

1061
                   as *mut libc::c_char,
1063
                   as *mut libc::c_char,
1062
            );
1064
            );
1063
            return;
1065
            return;
1064
        }
1066
        }
1065
    }
1067
    }
1066
    if check_y < 3i32 || check_y > LINES - 1i32 || check_x < 0i32 || check_x > COLS - 1i32 {
1068
    if check_y < 3i32
1069
        || check_y > win.get_max_y() - 1i32
1070
        || check_x < 0i32
1071
        || check_x > win.get_max_x() - 1i32
1072
    {
1067
        return;
1073
        return;
1068
    }
1074
    }
1069
    if screen[check_x as isize as usize][check_y as isize as usize] != -1i32 {
1075
    if screen[check_x as isize as usize][check_y as isize as usize] != -1i32 {
1070
        match screen[check_x as isize as usize][check_y as isize as usize] {
1076
        match screen[check_x as isize as usize][check_y as isize as usize] {
1071
            0 => {}
1077
            0 => {}

Finally, we handle a few special cases.

waddnstr takes a string argument, which in general could be any *const c_char. However, robotfindskitten calls it only on string literals, which lets us perform a more specialized rewrite that avoids unsafe C string conversions:

rewrite_expr
    'waddnstr(stdscr, __str as *const u8 as *const libc::c_char, __n)'
    "win.addnstr(::std::str::from_utf8(__str).unwrap().trim_end_matches('\0'),
                 __n as usize)" ;

Diff #38

src/robotfindskitten.rs
1124
       win.refresh();
1124
       win.refresh();
1125
        sleep(1i32 as libc::c_uint);
1125
        sleep(1i32 as libc::c_uint);
1126
        counter -= 1
1126
        counter -= 1
1127
    }
1127
    }
1128
    win.mv(1i32, 0i32);
1128
    win.mv(1i32, 0i32);
1129
    waddnstr(
1129
    win.addnstr(
1130
        stdscr,
1130
        ::std::str::from_utf8(b"You found kitten! Way to go, robot!\x00")
1131
        b"You found kitten! Way to go, robot!\x00" as *const u8 as *const libc::c_char,
1131
            .unwrap()
1132
            .trim_end_matches('\u{0}'),
1132
        -1i32,
1133
        -1i32 as usize,
1133
    );
1134
    );
1134
    win.refresh();
1135
    win.refresh();
1135
    finish(0i32);
1136
    finish(0i32);
1136
}
1137
}
1137
unsafe fn main_0(mut argc: libc::c_int, mut argv: *mut *mut libc::c_char) -> libc::c_int {
1138
unsafe fn main_0(mut argc: libc::c_int, mut argv: *mut *mut libc::c_char) -> libc::c_int {

intrflush has no pancurses equivalent, so we replace it with a no-op of the same type:

rewrite_expr 'intrflush(__e, __f)' '0' ;

Diff #39

src/robotfindskitten.rs
639
pub unsafe extern "C" fn initialize_ncurses() {
639
pub unsafe extern "C" fn initialize_ncurses() {
640
    signal(2i32, Some(finish));
640
    signal(2i32, Some(finish));
641
    initscr();
641
    initscr();
642
    win.keypad(0 != 1i32);
642
    win.keypad(0 != 1i32);
643
    ::pancurses::nonl();
643
    ::pancurses::nonl();
644
    intrflush(stdscr, 0 != 0i32);
644
    0;
645
    ::pancurses::noecho();
645
    ::pancurses::noecho();
646
    ::pancurses::cbreak();
646
    ::pancurses::cbreak();
647
    if ::pancurses::has_colors() {
647
    if ::pancurses::has_colors() {
648
        ::pancurses::start_color();
648
        ::pancurses::start_color();
649
        ::pancurses::init_pair(
649
        ::pancurses::init_pair(

That covers all of the "ordinary" ncurses functions used in robotfindskitten. The remaining subsections cover the more complex cases.

String formatting

We previously replaced calls to the ncurses printw and mvprintw string-formatting functions with code using Rust's safe string formatting macros. This removes unsafety from the call site, but uses wrapper functions (fmt_printw and fmt_mvprintw) that call unsafe code internally. But now that we are using the pancurses library, we can replace those wrappers with safer equivalents.

select target 'item(fmt_printw);' ;
create_item '
    fn fmt_printw(args: ::std::fmt::Arguments) -> libc::c_int {
        unsafe {
            win.printw(&format!("{}", args))
        }
    }
' after ;
delete_items ;
clear_marks ;

select target 'item(fmt_mvprintw);' ;
create_item '
    fn fmt_mvprintw(y: libc::c_int, x: libc::c_int,
                    args: ::std::fmt::Arguments) -> libc::c_int {
        unsafe {
            win.mvprintw(y, x, &format!("{}", args))
        }
    }
' after ;
delete_items ;
clear_marks ;

Diff #40

src/robotfindskitten.rs
7
    unused_mut
7
    unused_mut
8
)]
8
)]
9
#![feature(const_raw_ptr_to_usize_cast, extern_types, libc)]
9
#![feature(const_raw_ptr_to_usize_cast, extern_types, libc)]
10
static mut win: Option<::pancurses::Window> = None;
10
static mut win: Option<::pancurses::Window> = None;
11
extern crate c2rust_runtime;
11
extern crate c2rust_runtime;
12
extern crate libc;
12
extern crate libc;
13
extern crate pancurses;
13
extern crate pancurses;
14
extern "C" {
14
extern "C" {
15
    pub type ldat;
15
    pub type ldat;
16
    #[no_mangle]
16
    #[no_mangle]

90
       opts: *mut libc::c_void,
90
        opts: *mut libc::c_void,
91
    ) -> libc::c_int;
91
    ) -> libc::c_int;
92
    fn wattrset(win: *mut WINDOW, attrs: libc::c_int) -> libc::c_int;
92
    fn wattrset(win: *mut WINDOW, attrs: libc::c_int) -> libc::c_int;
93
}
93
}
94
fn fmt_mvprintw(y: libc::c_int, x: libc::c_int, args: ::std::fmt::Arguments) -> libc::c_int {
94
fn fmt_mvprintw(y: libc::c_int, x: libc::c_int, args: ::std::fmt::Arguments) -> libc::c_int {
95
    unsafe {
95
    unsafe { win.mvprintw(y, x, &format!("{}", args)) }
96
        ::mvprintw(
97
            y,
98
            x,
99
            b"%s\0" as *const u8 as *const libc::c_char,
100
            ::std::ffi::CString::new(format!("{}", args))
101
                .unwrap()
102
                .as_ptr(),
103
        )
104
    }
105
}
96
}
106
fn fmt_printw(args: ::std::fmt::Arguments) -> libc::c_int {
97
fn fmt_printw(args: ::std::fmt::Arguments) -> libc::c_int {
107
    unsafe {
98
    unsafe { win.printw(&format!("{}", args)) }
108
        ::printw(
109
            b"%s\0" as *const u8 as *const libc::c_char,
110
            ::std::ffi::CString::new(format!("{}", args))
111
                .unwrap()
112
                .as_ptr(),
113
        )
114
    }
115
}
99
}
116
fn fmt_printf(args: ::std::fmt::Arguments) -> libc::c_int {
100
fn fmt_printf(args: ::std::fmt::Arguments) -> libc::c_int {
117
    print!("{}", args);
101
    print!("{}", args);
118
    0
102
    0
119
}
103
}

The wrappers still use unsafe code to access win, a static mut, but no longer make FFI calls or manipulate raw C strings. When we later remove all static muts from the program, these functions will become entirely safe.

Input handling

Adapting ncurses-based input handling to use pancurses requires some extra care. The pancurses getch function returns a Rust enum, while the ncurses version simply returns an integer. robotfindskitten matches those integers against various ncurses keycode constants, which, after macro expansion, become integer literals in the Rust code.

The more idiomatic approach would be to replace each integer literal with the matching pancurses::Input enum variant when switching from ncurses getch to the pancurses version. However, we instead take the easier approach of converting pancurses::Input values back to ncurses integer keycodes, so the existing robotfindskitten input handling code can remain unchanged.

First, we inject a translation function from pancurses to ncurses keycodes:

select target 'item(initialize_ncurses);' ;
create_item '
    fn encode_input(inp: Option<::pancurses::Input>) -> libc::c_int {
        use ::pancurses::Input::*;
        let inp = match inp {
            Some(x) => x,
            None => return -1,
        };
        match inp {
            // TODO: unicode inputs in the range 256 .. 512 can
            // collide with ncurses special keycodes
            Character(c) => c as u32 as libc::c_int,
            Unknown(i) => i,
            special => {
                let idx = ::pancurses::SPECIAL_KEY_CODES.iter()
                    .position(|&k| k == special).unwrap();
                let code = idx as i32 + ::pancurses::KEY_OFFSET;
                if code > ::pancurses::KEY_F15 {
                    code + 48
                } else {
                    code
                }
            },
        }
    }
' after ;

Diff #41

src/robotfindskitten.rs
619
 *Function definitions
619
 *Function definitions
620
 */
620
 */
621
/*Initialization and setup functions*/
621
/*Initialization and setup functions*/
622
#[no_mangle]
622
#[no_mangle]
623
pub unsafe extern "C" fn initialize_ncurses() {
623
pub unsafe extern "C" fn initialize_ncurses() {
624
    signal(2i32, Some(finish));
624
    signal(2i32, Some(finish));
625
    initscr();
625
    initscr();
626
    win.keypad(0 != 1i32);
626
    win.keypad(0 != 1i32);
627
    ::pancurses::nonl();
627
    ::pancurses::nonl();

670
            3i32 as libc::c_short,
670
           3i32 as libc::c_short,
671
            0i32 as libc::c_short,
671
            0i32 as libc::c_short,
672
        );
672
        );
673
    };
673
    };
674
}
674
}
675
fn encode_input(inp: Option<::pancurses::Input>) -> libc::c_int {
676
    use pancurses::Input::*;
677
    let inp = match inp {
678
        Some(x) => x,
679
        None => return -1,
680
    };
681
    match inp {
682
        // TODO: unicode inputs in the range 256 .. 512 can
683
        // collide with ncurses special keycodes
684
        Character(c) => c as u32 as libc::c_int,
685
        Unknown(i) => i,
686
        special => {
687
            let idx = ::pancurses::SPECIAL_KEY_CODES
688
                .iter()
689
                .position(|&k| k == special)
690
                .unwrap();
691
            let code = idx as i32 + ::pancurses::KEY_OFFSET;
692
            if code > ::pancurses::KEY_F15 {
693
                code + 48
694
            } else {
695
                code
696
            }
697
        }
698
    }
699
}
675
unsafe extern "C" fn finish(mut sig: libc::c_int) {
700
unsafe extern "C" fn finish(mut sig: libc::c_int) {
676
    ::pancurses::endwin();
701
    ::pancurses::endwin();
677
    fmt_printf(format_args!(
702
    fmt_printf(format_args!(
678
        "{:}{:}{:}",
703
        "{:}{:}{:}",
679
        27i32 as u8 as char, '(' as i32 as u8 as char, 'B' as i32 as u8 as char
704
        27i32 as u8 as char, '(' as i32 as u8 as char, 'B' as i32 as u8 as char

Then, we translate ncurses wgetch calls to use the pancurses getch method, wrapping the result in encode_input to keep the results unchanged.

rewrite_expr 'wgetch(stdscr)' '::encode_input(win.getch())' ;

Diff #42

src/robotfindskitten.rs
985
    fmt_printw(format_args!(
985
    fmt_printw(format_args!(
986
        "the Esc key. See the documentation for more information.\n\n"
986
        "the Esc key. See the documentation for more information.\n\n"
987
    ));
987
    ));
988
    fmt_printw(format_args!("Press any key to start.\n"));
988
    fmt_printw(format_args!("Press any key to start.\n"));
989
    win.refresh();
989
    win.refresh();
990
    dummy = wgetch(stdscr) as libc::c_char;
990
    dummy = ::encode_input(win.getch()) as libc::c_char;
991
    win.clear();
991
    win.clear();
992
}
992
}
993
#[no_mangle]
993
#[no_mangle]
994
pub unsafe extern "C" fn draw_in_place(mut o: screen_object) {
994
pub unsafe extern "C" fn draw_in_place(mut o: screen_object) {
995
    full_draw(o, 0 != 1i32);
995
    full_draw(o, 0 != 1i32);

998
#[no_mangle]
998
#[no_mangle]
999
pub unsafe extern "C" fn play_game() {
999
pub unsafe extern "C" fn play_game() {
1000
    let mut old_x: libc::c_int = robot.x;
1000
    let mut old_x: libc::c_int = robot.x;
1001
    let mut old_y: libc::c_int = robot.y;
1001
    let mut old_y: libc::c_int = robot.y;
1002
    let mut input: libc::c_int = 0;
1002
    let mut input: libc::c_int = 0;
1003
    input = wgetch(stdscr);
1003
    input = ::encode_input(win.getch());
1004
    while input != 27i32 && input != 'q' as i32 && input != 'Q' as i32 {
1004
    while input != 27i32 && input != 'q' as i32 && input != 'Q' as i32 {
1005
        process_input(input);
1005
        process_input(input);
1006
        if !(old_x == robot.x && old_y == robot.y) {
1006
        if !(old_x == robot.x && old_y == robot.y) {
1007
            if win.mv(old_y, old_x) == -1i32 {
1007
            if win.mv(old_y, old_x) == -1i32 {
1008
            } else {
1008
            } else {

1013
            win.refresh();
1013
            win.refresh();
1014
            screen[robot.x as isize as usize][robot.y as isize as usize] = 0i32;
1014
            screen[robot.x as isize as usize][robot.y as isize as usize] = 0i32;
1015
            old_x = robot.x;
1015
            old_x = robot.x;
1016
            old_y = robot.y
1016
            old_y = robot.y
1017
        }
1017
        }
1018
        input = wgetch(stdscr)
1018
        input = ::encode_input(win.getch())
1019
    }
1019
    }
1020
    message(b"Bye!\x00" as *const u8 as *const libc::c_char as *mut libc::c_char);
1020
    message(b"Bye!\x00" as *const u8 as *const libc::c_char as *mut libc::c_char);
1021
    win.refresh();
1021
    win.refresh();
1022
    finish(0i32);
1022
    finish(0i32);
1023
}
1023
}

Final steps

As mentioned previously, we use win to obtain the current window object throughout the ncurses refactoring process, even though win is actually an Option<Window>, not a Window. Now that we are done with all the rewrites, we can update thote uses to access the Window properly:

rewrite_expr 'win' 'win.as_ref().unwrap()' ;

Diff #43

src/robotfindskitten.rs
90
        opts: *mut libc::c_void,
90
        opts: *mut libc::c_void,
91
    ) -> libc::c_int;
91
    ) -> libc::c_int;
92
    fn wattrset(win: *mut WINDOW, attrs: libc::c_int) -> libc::c_int;
92
    fn wattrset(win: *mut WINDOW, attrs: libc::c_int) -> libc::c_int;
93
}
93
}
94
fn fmt_mvprintw(y: libc::c_int, x: libc::c_int, args: ::std::fmt::Arguments) -> libc::c_int {
94
fn fmt_mvprintw(y: libc::c_int, x: libc::c_int, args: ::std::fmt::Arguments) -> libc::c_int {
95
    unsafe { win.mvprintw(y, x, &format!("{}", args)) }
95
    unsafe { win.as_ref().unwrap().mvprintw(y, x, &format!("{}", args)) }
96
}
96
}
97
fn fmt_printw(args: ::std::fmt::Arguments) -> libc::c_int {
97
fn fmt_printw(args: ::std::fmt::Arguments) -> libc::c_int {
98
    unsafe { win.printw(&format!("{}", args)) }
98
    unsafe { win.as_ref().unwrap().printw(&format!("{}", args)) }
99
}
99
}
100
fn fmt_printf(args: ::std::fmt::Arguments) -> libc::c_int {
100
fn fmt_printf(args: ::std::fmt::Arguments) -> libc::c_int {
101
    print!("{}", args);
101
    print!("{}", args);
102
    0
102
    0
103
}
103
}

621
/*Initialization and setup functions*/
621
/*Initialization and setup functions*/
622
#[no_mangle]
622
#[no_mangle]
623
pub unsafe extern "C" fn initialize_ncurses() {
623
pub unsafe extern "C" fn initialize_ncurses() {
624
    signal(2i32, Some(finish));
624
    signal(2i32, Some(finish));
625
    initscr();
625
    initscr();
626
    win.keypad(0 != 1i32);
626
    win.as_ref().unwrap().keypad(0 != 1i32);
627
    ::pancurses::nonl();
627
    ::pancurses::nonl();
628
    0;
628
    0;
629
    ::pancurses::noecho();
629
    ::pancurses::noecho();
630
    ::pancurses::cbreak();
630
    ::pancurses::cbreak();
631
    if ::pancurses::has_colors() {
631
    if ::pancurses::has_colors() {

718
    };
718
    };
719
    let mut i: libc::c_int = 0i32;
719
    let mut i: libc::c_int = 0i32;
720
    screen = unsafe {
720
    screen = unsafe {
721
        ::c2rust_runtime::CArray::alloc(
721
        ::c2rust_runtime::CArray::alloc(
722
            (::std::mem::size_of::<*mut libc::c_int>() as libc::c_ulong)
722
            (::std::mem::size_of::<*mut libc::c_int>() as libc::c_ulong)
723
                .wrapping_mul((win.get_max_x() - 1i32 + 1i32) as libc::c_ulong)
723
                .wrapping_mul((win.as_ref().unwrap().get_max_x() - 1i32 + 1i32) as libc::c_ulong)
724
                as usize
724
                as usize
725
                / ::std::mem::size_of::<::c2rust_runtime::CArray<i32>>(),
725
                / ::std::mem::size_of::<::c2rust_runtime::CArray<i32>>(),
726
        )
726
        )
727
    };
727
    };
728
    i = 0i32;
728
    i = 0i32;
729
    while i < win.get_max_x() - 1i32 + 1i32 {
729
    while i < win.as_ref().unwrap().get_max_x() - 1i32 + 1i32 {
730
        let ref mut fresh0 = screen[i as isize as usize];
730
        let ref mut fresh0 = screen[i as isize as usize];
731
        *fresh0 = unsafe {
731
        *fresh0 = unsafe {
732
            ::c2rust_runtime::CArray::alloc(
732
            ::c2rust_runtime::CArray::alloc(
733
                (::std::mem::size_of::() as libc::c_ulong)
733
                (::std::mem::size_of::() as libc::c_ulong).wrapping_mul(
734
                    .wrapping_mul((win.get_max_y() - 1i32 + 1i32) as libc::c_ulong)
734
                    (win.as_ref().unwrap().get_max_y() - 1i32 + 1i32) as libc::c_ulong,
735
                    as usize
735
                ) as usize
736
                    / ::std::mem::size_of::(),
736
                    / ::std::mem::size_of::(),
737
            )
737
            )
738
        };
738
        };
739
        i += 1
739
        i += 1
740
    }
740
    }

742
    empty.y = -1i32;
742
    empty.y = -1i32;
743
    empty.color = 0i32;
743
    empty.color = 0i32;
744
    empty.bold = 0 != 0i32;
744
    empty.bold = 0 != 0i32;
745
    empty.character = ' ' as i32 as libc::c_char;
745
    empty.character = ' ' as i32 as libc::c_char;
746
    counter = 0i32;
746
    counter = 0i32;
747
    while counter <= win.get_max_x() - 1i32 {
747
    while counter <= win.as_ref().unwrap().get_max_x() - 1i32 {
748
        counter2 = 0i32;
748
        counter2 = 0i32;
749
        while counter2 <= win.get_max_y() - 1i32 {
749
        while counter2 <= win.as_ref().unwrap().get_max_y() - 1i32 {
750
            screen[counter as isize as usize][counter2 as isize as usize] = -1i32;
750
            screen[counter as isize as usize][counter2 as isize as usize] = -1i32;
751
            counter2 += 1
751
            counter2 += 1
752
        }
752
        }
753
        counter += 1
753
        counter += 1
754
    }
754
    }

782
#[no_mangle]
782
#[no_mangle]
783
pub static mut screen: ::c2rust_runtime::CArray<::c2rust_runtime::CArray> =
783
pub static mut screen: ::c2rust_runtime::CArray<::c2rust_runtime::CArray> =
784
    unsafe { ::c2rust_runtime::CArray::empty() };
784
    unsafe { ::c2rust_runtime::CArray::empty() };
785
#[no_mangle]
785
#[no_mangle]
786
pub unsafe extern "C" fn initialize_robot() {
786
pub unsafe extern "C" fn initialize_robot() {
787
    robot.x = rand() % (win.get_max_x() - 1i32) + 1i32;
787
    robot.x = rand() % (win.as_ref().unwrap().get_max_x() - 1i32) + 1i32;
788
    robot.y = rand() % (win.get_max_y() - 1i32 - 3i32 + 1i32) + 3i32;
788
    robot.y = rand() % (win.as_ref().unwrap().get_max_y() - 1i32 - 3i32 + 1i32) + 3i32;
789
    robot.character = '#' as i32 as libc::c_char;
789
    robot.character = '#' as i32 as libc::c_char;
790
    robot.color = 0i32;
790
    robot.color = 0i32;
791
    robot.bold = 0 != 0i32;
791
    robot.bold = 0 != 0i32;
792
    screen[robot.x as isize as usize][robot.y as isize as usize] = 0i32;
792
    screen[robot.x as isize as usize][robot.y as isize as usize] = 0i32;
793
}
793
}

801
    character: 0,
801
    character: 0,
802
};
802
};
803
#[no_mangle]
803
#[no_mangle]
804
pub unsafe extern "C" fn initialize_kitten() {
804
pub unsafe extern "C" fn initialize_kitten() {
805
    loop {
805
    loop {
806
        kitten.x = rand() % (win.get_max_x() - 1i32) + 1i32;
806
        kitten.x = rand() % (win.as_ref().unwrap().get_max_x() - 1i32) + 1i32;
807
        kitten.y = rand() % (win.get_max_y() - 1i32 - 3i32 + 1i32) + 3i32;
807
        kitten.y = rand() % (win.as_ref().unwrap().get_max_y() - 1i32 - 3i32 + 1i32) + 3i32;
808
        if !(screen[kitten.x as isize as usize][kitten.y as isize as usize] != -1i32) {
808
        if !(screen[kitten.x as isize as usize][kitten.y as isize as usize] != -1i32) {
809
            break;
809
            break;
810
        }
810
        }
811
    }
811
    }
812
    loop {
812
    loop {

850
            if !(0 == validchar(bogus[counter as usize].character)) {
850
            if !(0 == validchar(bogus[counter as usize].character)) {
851
                break;
851
                break;
852
            }
852
            }
853
        }
853
        }
854
        loop {
854
        loop {
855
            bogus[counter as usize].x = rand() % (win.get_max_x() - 1i32) + 1i32;
855
            bogus[counter as usize].x = rand() % (win.as_ref().unwrap().get_max_x() - 1i32) + 1i32;
856
            bogus[counter as usize].y = rand() % (win.get_max_y() - 1i32 - 3i32 + 1i32) + 3i32;
856
            bogus[counter as usize].y =
857
                rand() % (win.as_ref().unwrap().get_max_y() - 1i32 - 3i32 + 1i32) + 3i32;
857
            if !(screen[bogus[counter as usize].x as isize as usize]
858
            if !(screen[bogus[counter as usize].x as isize as usize]
858
                [bogus[counter as usize].y as isize as usize]
859
                [bogus[counter as usize].y as isize as usize]
859
                != -1i32)
860
                != -1i32)
860
            {
861
            {
861
                break;
862
                break;

890
                .to_str()
891
                .to_str()
891
                .unwrap()
892
                .unwrap()
892
        }),
893
        }),
893
    );
894
    );
894
    counter = 0i32;
895
    counter = 0i32;
895
    while counter <= win.get_max_x() - 1i32 {
896
    while counter <= win.as_ref().unwrap().get_max_x() - 1i32 {
896
        fmt_printw(format_args!("{:}", 95i32 as u8 as char));
897
        fmt_printw(format_args!("{:}", 95i32 as u8 as char));
897
        counter += 1
898
        counter += 1
898
    }
899
    }
899
    counter = 0i32;
900
    counter = 0i32;
900
    while counter < num_bogus {
901
    while counter < num_bogus {
901
        draw(bogus[counter as usize]);
902
        draw(bogus[counter as usize]);
902
        counter += 1
903
        counter += 1
903
    }
904
    }
904
    draw(kitten);
905
    draw(kitten);
905
    draw(robot);
906
    draw(robot);
906
    win.refresh();
907
    win.as_ref().unwrap().refresh();
907
}
908
}
908
#[no_mangle]
909
#[no_mangle]
909
pub unsafe extern "C" fn draw(mut o: screen_object) {
910
pub unsafe extern "C" fn draw(mut o: screen_object) {
910
    full_draw(o, 0 != 0i32);
911
    full_draw(o, 0 != 0i32);
911
}
912
}

934
        new |= 1u64 << 14i32 + 8i32
935
        new |= 1u64 << 14i32 + 8i32
935
    }
936
    }
936
    if o.bold {
937
    if o.bold {
937
        new |= 1u64 << 13i32 + 8i32
938
        new |= 1u64 << 13i32 + 8i32
938
    }
939
    }
940
    win.as_ref()
941
        .unwrap()
939
    win.attrset(new as libc::c_int as ::pancurses::chtype);
942
        .attrset(new as libc::c_int as ::pancurses::chtype);
940
    if in_place {
943
    if in_place {
941
        fmt_printw(format_args!(
944
        fmt_printw(format_args!(
942
            "{:}",
945
            "{:}",
943
            o.character as libc::c_int as u8 as char
946
            o.character as libc::c_int as u8 as char
944
        ));
947
        ));

946
        fmt_mvprintw(
949
        fmt_mvprintw(
947
            o.y,
950
            o.y,
948
            o.x,
951
            o.x,
949
            format_args!("{:}", o.character as libc::c_int as u8 as char),
952
            format_args!("{:}", o.character as libc::c_int as u8 as char),
950
        );
953
        );
951
        win.mv(o.y, o.x);
954
        win.as_ref().unwrap().mv(o.y, o.x);
952
    }
955
    }
956
    win.as_ref()
957
        .unwrap()
953
    win.attrset(old as libc::c_int as ::pancurses::chtype);
958
        .attrset(old as libc::c_int as ::pancurses::chtype);
954
}
959
}
955
#[no_mangle]
960
#[no_mangle]
956
pub unsafe extern "C" fn instructions() {
961
pub unsafe extern "C" fn instructions() {
957
    let mut dummy: libc::c_char = 0;
962
    let mut dummy: libc::c_char = 0;
958
    fmt_mvprintw(
963
    fmt_mvprintw(

984
    ));
989
    ));
985
    fmt_printw(format_args!(
990
    fmt_printw(format_args!(
986
        "the Esc key. See the documentation for more information.\n\n"
991
        "the Esc key. See the documentation for more information.\n\n"
987
    ));
992
    ));
988
    fmt_printw(format_args!("Press any key to start.\n"));
993
    fmt_printw(format_args!("Press any key to start.\n"));
989
    win.refresh();
994
    win.as_ref().unwrap().refresh();
990
    dummy = ::encode_input(win.getch()) as libc::c_char;
995
    dummy = ::encode_input(win.as_ref().unwrap().getch()) as libc::c_char;
991
    win.clear();
996
    win.as_ref().unwrap().clear();
992
}
997
}
993
#[no_mangle]
998
#[no_mangle]
994
pub unsafe extern "C" fn draw_in_place(mut o: screen_object) {
999
pub unsafe extern "C" fn draw_in_place(mut o: screen_object) {
995
    full_draw(o, 0 != 1i32);
1000
    full_draw(o, 0 != 1i32);
996
}
1001
}

998
#[no_mangle]
1003
#[no_mangle]
999
pub unsafe extern "C" fn play_game() {
1004
pub unsafe extern "C" fn play_game() {
1000
    let mut old_x: libc::c_int = robot.x;
1005
    let mut old_x: libc::c_int = robot.x;
1001
    let mut old_y: libc::c_int = robot.y;
1006
    let mut old_y: libc::c_int = robot.y;
1002
    let mut input: libc::c_int = 0;
1007
    let mut input: libc::c_int = 0;
1003
    input = ::encode_input(win.getch());
1008
    input = ::encode_input(win.as_ref().unwrap().getch());
1004
    while input != 27i32 && input != 'q' as i32 && input != 'Q' as i32 {
1009
    while input != 27i32 && input != 'q' as i32 && input != 'Q' as i32 {
1005
        process_input(input);
1010
        process_input(input);
1006
        if !(old_x == robot.x && old_y == robot.y) {
1011
        if !(old_x == robot.x && old_y == robot.y) {
1007
            if win.mv(old_y, old_x) == -1i32 {
1012
            if win.as_ref().unwrap().mv(old_y, old_x) == -1i32 {
1008
            } else {
1013
            } else {
1009
                win.addch(' ' as i32 as chtype);
1014
                win.as_ref().unwrap().addch(' ' as i32 as chtype);
1010
            };
1015
            };
1011
            screen[old_x as isize as usize][old_y as isize as usize] = -1i32;
1016
            screen[old_x as isize as usize][old_y as isize as usize] = -1i32;
1012
            draw(robot);
1017
            draw(robot);
1013
            win.refresh();
1018
            win.as_ref().unwrap().refresh();
1014
            screen[robot.x as isize as usize][robot.y as isize as usize] = 0i32;
1019
            screen[robot.x as isize as usize][robot.y as isize as usize] = 0i32;
1015
            old_x = robot.x;
1020
            old_x = robot.x;
1016
            old_y = robot.y
1021
            old_y = robot.y
1017
        }
1022
        }
1018
        input = ::encode_input(win.getch())
1023
        input = ::encode_input(win.as_ref().unwrap().getch())
1019
    }
1024
    }
1020
    message(b"Bye!\x00" as *const u8 as *const libc::c_char as *mut libc::c_char);
1025
    message(b"Bye!\x00" as *const u8 as *const libc::c_char as *mut libc::c_char);
1021
    win.refresh();
1026
    win.as_ref().unwrap().refresh();
1022
    finish(0i32);
1027
    finish(0i32);
1023
}
1028
}
1024
#[no_mangle]
1029
#[no_mangle]
1025
pub unsafe extern "C" fn message(mut message_0: *mut libc::c_char) {
1030
pub unsafe extern "C" fn message(mut message_0: *mut libc::c_char) {
1026
    win.mv(1i32, 0i32);
1031
    win.as_ref().unwrap().mv(1i32, 0i32);
1027
    win.clrtoeol();
1032
    win.as_ref().unwrap().clrtoeol();
1028
    fmt_mvprintw(
1033
    fmt_mvprintw(
1029
        1i32,
1034
        1i32,
1030
        0i32,
1035
        0i32,
1031
        format_args!("{:.*}", win.get_max_x() as usize, unsafe {
1036
        format_args!(
1037
            "{:.*}",
1038
            win.as_ref().unwrap().get_max_x() as usize,
1039
            unsafe {
1032
            ::std::ffi::CStr::from_ptr(message_0 as *const libc::c_char)
1040
                ::std::ffi::CStr::from_ptr(message_0 as *const libc::c_char)
1033
                .to_str()
1041
                    .to_str()
1034
                .unwrap()
1042
                    .unwrap()
1043
            }
1035
        }),
1044
        ),
1036
    );
1045
    );
1037
    win.mv(robot.y, robot.x);
1046
    win.as_ref().unwrap().mv(robot.y, robot.x);
1038
    win.refresh();
1047
    win.as_ref().unwrap().refresh();
1039
}
1048
}
1040
#[no_mangle]
1049
#[no_mangle]
1041
pub unsafe extern "C" fn process_input(mut input: libc::c_int) {
1050
pub unsafe extern "C" fn process_input(mut input: libc::c_int) {
1042
    let mut check_x: libc::c_int = robot.x;
1051
    let mut check_x: libc::c_int = robot.x;
1043
    let mut check_y: libc::c_int = robot.y;
1052
    let mut check_y: libc::c_int = robot.y;
1044
    match input {
1053
    match input {
1045
        12 => {
1054
        12 => {
1046
            win.refresh();
1055
            win.as_ref().unwrap().refresh();
1047
        }
1056
        }
1048
        259 | 107 | 75 | 16 => check_y -= 1,
1057
        259 | 107 | 75 | 16 => check_y -= 1,
1049
        262 | 121 | 89 => {
1058
        262 | 121 | 89 => {
1050
            check_x -= 1;
1059
            check_x -= 1;
1051
            check_y -= 1
1060
            check_y -= 1

1073
            );
1082
            );
1074
            return;
1083
            return;
1075
        }
1084
        }
1076
    }
1085
    }
1077
    if check_y < 3i32
1086
    if check_y < 3i32
1078
        || check_y > win.get_max_y() - 1i32
1087
        || check_y > win.as_ref().unwrap().get_max_y() - 1i32
1079
        || check_x < 0i32
1088
        || check_x < 0i32
1080
        || check_x > win.get_max_x() - 1i32
1089
        || check_x > win.as_ref().unwrap().get_max_x() - 1i32
1081
    {
1090
    {
1082
        return;
1091
        return;
1083
    }
1092
    }
1084
    if screen[check_x as isize as usize][check_y as isize as usize] != -1i32 {
1093
    if screen[check_x as isize as usize][check_y as isize as usize] != -1i32 {
1085
        match screen[check_x as isize as usize][check_y as isize as usize] {
1094
        match screen[check_x as isize as usize][check_y as isize as usize] {
1086
            0 => {}
1095
            0 => {}
1087
            1 => {
1096
            1 => {
1088
                /*We didn't move, or we're stuck in a
1097
                /*We didn't move, or we're stuck in a
1089
                time warp or something.*/
1098
                time warp or something.*/
1090
                win.mv(1i32, 0i32);
1099
                win.as_ref().unwrap().mv(1i32, 0i32);
1091
                win.clrtoeol();
1100
                win.as_ref().unwrap().clrtoeol();
1092
                play_animation(input);
1101
                play_animation(input);
1093
            }
1102
            }
1094
            _ => {
1103
            _ => {
1095
                message(
1104
                message(
1096
                    messages[bogus_messages[(screen[check_x as isize as usize]
1105
                    messages[bogus_messages[(screen[check_x as isize as usize]

1108
#[no_mangle]
1117
#[no_mangle]
1109
pub unsafe extern "C" fn play_animation(mut input: libc::c_int) {
1118
pub unsafe extern "C" fn play_animation(mut input: libc::c_int) {
1110
    let mut counter: libc::c_int = 0;
1119
    let mut counter: libc::c_int = 0;
1111
    counter = 4i32;
1120
    counter = 4i32;
1112
    while counter > 0i32 {
1121
    while counter > 0i32 {
1113
        if win.mv(1i32, 50i32 + counter + 1i32) == -1i32 {
1122
        if win.as_ref().unwrap().mv(1i32, 50i32 + counter + 1i32) == -1i32 {
1114
        } else {
1123
        } else {
1115
            win.addch(' ' as i32 as chtype);
1124
            win.as_ref().unwrap().addch(' ' as i32 as chtype);
1116
        };
1125
        };
1117
        win.mv(1i32, 50i32 + counter);
1126
        win.as_ref().unwrap().mv(1i32, 50i32 + counter);
1118
        if input == 0o405i32 || input == 0o402i32 || input == 0o540i32 || input == 0o535i32 {
1127
        if input == 0o405i32 || input == 0o402i32 || input == 0o540i32 || input == 0o535i32 {
1119
            draw_in_place(kitten);
1128
            draw_in_place(kitten);
1120
        } else {
1129
        } else {
1121
            draw_in_place(robot);
1130
            draw_in_place(robot);
1122
        }
1131
        }
1123
        if win.mv(1i32, 50i32 - counter) == -1i32 {
1132
        if win.as_ref().unwrap().mv(1i32, 50i32 - counter) == -1i32 {
1124
        } else {
1133
        } else {
1125
            win.addch(' ' as i32 as chtype);
1134
            win.as_ref().unwrap().addch(' ' as i32 as chtype);
1126
        };
1135
        };
1127
        win.mv(1i32, 50i32 - counter + 1i32);
1136
        win.as_ref().unwrap().mv(1i32, 50i32 - counter + 1i32);
1128
        if input == 0o405i32 || input == 0o402i32 || input == 0o540i32 || input == 0o535i32 {
1137
        if input == 0o405i32 || input == 0o402i32 || input == 0o540i32 || input == 0o535i32 {
1129
            draw_in_place(robot);
1138
            draw_in_place(robot);
1130
        } else {
1139
        } else {
1131
            draw_in_place(kitten);
1140
            draw_in_place(kitten);
1132
        }
1141
        }
1133
        win.refresh();
1142
        win.as_ref().unwrap().refresh();
1134
        sleep(1i32 as libc::c_uint);
1143
        sleep(1i32 as libc::c_uint);
1135
        counter -= 1
1144
        counter -= 1
1136
    }
1145
    }
1137
    win.mv(1i32, 0i32);
1146
    win.as_ref().unwrap().mv(1i32, 0i32);
1138
    win.addnstr(
1147
    win.as_ref().unwrap().addnstr(
1139
        ::std::str::from_utf8(b"You found kitten! Way to go, robot!\x00")
1148
        ::std::str::from_utf8(b"You found kitten! Way to go, robot!\x00")
1140
            .unwrap()
1149
            .unwrap()
1141
            .trim_end_matches('\u{0}'),
1150
            .trim_end_matches('\u{0}'),
1142
        -1i32 as usize,
1151
        -1i32 as usize,
1143
    );
1152
    );
1144
    win.refresh();
1153
    win.as_ref().unwrap().refresh();
1145
    finish(0i32);
1154
    finish(0i32);
1146
}
1155
}
1147
unsafe fn main_0(mut argc: libc::c_int, mut argv: *mut *mut libc::c_char) -> libc::c_int {
1156
unsafe fn main_0(mut argc: libc::c_int, mut argv: *mut *mut libc::c_char) -> libc::c_int {
1148
    if argc == 1i32 {
1157
    if argc == 1i32 {
1149
        num_bogus = 20i32
1158
        num_bogus = 20i32

The final step is to initialize win. This corresponds to the call to the ncurses initscr initialization function:

rewrite_expr 'initscr()' 'win = Some(::pancurses::initscr())' ;

Diff #44

src/robotfindskitten.rs
620
 */
620
 */
621
/*Initialization and setup functions*/
621
/*Initialization and setup functions*/
622
#[no_mangle]
622
#[no_mangle]
623
pub unsafe extern "C" fn initialize_ncurses() {
623
pub unsafe extern "C" fn initialize_ncurses() {
624
    signal(2i32, Some(finish));
624
    signal(2i32, Some(finish));
625
    initscr();
625
    win = Some(::pancurses::initscr());
626
    win.as_ref().unwrap().keypad(0 != 1i32);
626
    win.as_ref().unwrap().keypad(0 != 1i32);
627
    ::pancurses::nonl();
627
    ::pancurses::nonl();
628
    0;
628
    0;
629
    ::pancurses::noecho();
629
    ::pancurses::noecho();
630
    ::pancurses::cbreak();
630
    ::pancurses::cbreak();

We save this for last only so that the win to win.as_ref().unwrap() rewrite doesn't produce an erroneous assignment win.as_ref().unwrap() = ....

At this point, we are done with the current refactoring step: robotfindskitten has been fully adapted to use the safe pancurses API in place of raw ncurses FFI calls.

commit

Diff #45

src/robotfindskitten.rs
619
 *Function definitions
619
 *Function definitions
620
 */
620
 */
621
/*Initialization and setup functions*/
621
/*Initialization and setup functions*/
622
#[no_mangle]
622
#[no_mangle]
623
pub unsafe extern "C" fn initialize_ncurses() {
623
pub unsafe extern "C" fn initialize_ncurses() {
624
    signal(2i32, Some(finish));
624
    signal(2i32, Some(finish));
625
    win = Some(::pancurses::initscr());
625
    win = Some(::pancurses::initscr());
626
    win.as_ref().unwrap().keypad(0 != 1i32);
626
    win.as_ref().unwrap().keypad(0 != 1i32);
627
    ::pancurses::nonl();
627
    ::pancurses::nonl();

672
       );
672
        );
673
    };
673
    };
674
}
674
}
675
fn encode_input(inp: Option<::pancurses::Input>) -> libc::c_int {
675
fn encode_input(inp: Option<::pancurses::Input>) -> libc::c_int {
676
    use pancurses::Input::*;
676
    use pancurses::Input::*;
677
    let inp = match inp {
677
    let inp = match inp {
678
        Some(x) => x,
678
        Some(x) => x,
679
        None => return -1,
679
        None => return -1,
680
    };
680
    };

Moving global state to the stack

robotfindskitten uses global variables - static muts in Rust - to store the game state. Accessing these globals is unsafe, due to the difficulty of preventing simultaneous borrowing and mutation. In this refactoring step, we move the global state onto the stack and pass it by reference to every function that needs it, which allows the borrow checker to analyze its usage and ensure safety.

Most of the work in this step is handled by the static_to_local_ref refactoring command. This command identifies all functions that use a given static, and modifies those functions to access the global through a reference (passed as an argument to the function) instead of accessing it directly. (See the static_to_local_ref command documentation for examples.)

However, running static_to_local_ref separately on each of robotfindskitten's seven global variables would add up to seven new arguments to many of robotfindskitten's functions, making their signatures difficult to read. Instead, we proceed in two steps. First, we gather up all the global variables into a single global struct. Then, we run static_to_local_ref on just the struct, achieving safety while adding only a single new argument to each affected function.

We collect the statics into a struct using static_collect_to_struct:

select target 'crate; child(static && mut);' ;
static_collect_to_struct State S

Diff #46

src/robotfindskitten.rs
5
    non_snake_case,
5
    non_snake_case,
6
    non_upper_case_globals,
6
    non_upper_case_globals,
7
    unused_mut
7
    unused_mut
8
)]
8
)]
9
#![feature(const_raw_ptr_to_usize_cast, extern_types, libc)]
9
#![feature(const_raw_ptr_to_usize_cast, extern_types, libc)]
10
struct State {
10
static mut win: Option<::pancurses::Window> = None;
11
    win: Option<::pancurses::Window>,
12
    bogus: [screen_object; 406],
13
    bogus_messages: [libc::c_int; 406],
14
    used_messages: [libc::c_int; 406],
15
    screen: ::c2rust_runtime::CArray<::c2rust_runtime::CArray>,
16
    robot: screen_object,
17
    kitten: screen_object,
18
    num_bogus: libc::c_int,
19
}
20
static mut S: State = State {
21
    win: None,
22
    bogus: [screen_object {
23
        x: 0,
24
        y: 0,
25
        color: 0,
26
        bold: false,
27
        character: 0,
28
    }; 406],
29
    bogus_messages: [0; 406],
30
    used_messages: [0; 406],
31
    screen: unsafe { ::c2rust_runtime::CArray::empty() },
32
    robot: screen_object {
33
        x: 0,
34
        y: 0,
35
        color: 0,
36
        bold: false,
37
        character: 0,
38
    },
39
    kitten: screen_object {
40
        x: 0,
41
        y: 0,
42
        color: 0,
43
        bold: false,
44
        character: 0,
45
    },
46
    num_bogus: 0,
47
};
11
extern crate c2rust_runtime;
48
extern crate c2rust_runtime;
12
extern crate libc;
49
extern crate libc;
13
extern crate pancurses;
50
extern crate pancurses;
14
extern "C" {
51
extern "C" {
15
    pub type ldat;
52
    pub type ldat;

90
        opts: *mut libc::c_void,
127
        opts: *mut libc::c_void,
91
    ) -> libc::c_int;
128
    ) -> libc::c_int;
92
    fn wattrset(win: *mut WINDOW, attrs: libc::c_int) -> libc::c_int;
129
    fn wattrset(win: *mut WINDOW, attrs: libc::c_int) -> libc::c_int;
93
}
130
}
94
fn fmt_mvprintw(y: libc::c_int, x: libc::c_int, args: ::std::fmt::Arguments) -> libc::c_int {
131
fn fmt_mvprintw(y: libc::c_int, x: libc::c_int, args: ::std::fmt::Arguments) -> libc::c_int {
95
    unsafe { win.as_ref().unwrap().mvprintw(y, x, &format!("{}", args)) }
132
    unsafe { S.win.as_ref().unwrap().mvprintw(y, x, &format!("{}", args)) }
96
}
133
}
97
fn fmt_printw(args: ::std::fmt::Arguments) -> libc::c_int {
134
fn fmt_printw(args: ::std::fmt::Arguments) -> libc::c_int {
98
    unsafe { win.as_ref().unwrap().printw(&format!("{}", args)) }
135
    unsafe { S.win.as_ref().unwrap().printw(&format!("{}", args)) }
99
}
136
}
100
fn fmt_printf(args: ::std::fmt::Arguments) -> libc::c_int {
137
fn fmt_printf(args: ::std::fmt::Arguments) -> libc::c_int {
101
    print!("{}", args);
138
    print!("{}", args);
102
    0
139
    0
103
}
140
}

620
 */
657
 */
621
/*Initialization and setup functions*/
658
/*Initialization and setup functions*/
622
#[no_mangle]
659
#[no_mangle]
623
pub unsafe extern "C" fn initialize_ncurses() {
660
pub unsafe extern "C" fn initialize_ncurses() {
624
    signal(2i32, Some(finish));
661
    signal(2i32, Some(finish));
625
    win = Some(::pancurses::initscr());
662
    S.win = Some(::pancurses::initscr());
626
    win.as_ref().unwrap().keypad(0 != 1i32);
663
    S.win.as_ref().unwrap().keypad(0 != 1i32);
627
    ::pancurses::nonl();
664
    ::pancurses::nonl();
628
    0;
665
    0;
629
    ::pancurses::noecho();
666
    ::pancurses::noecho();
630
    ::pancurses::cbreak();
667
    ::pancurses::cbreak();
631
    if ::pancurses::has_colors() {
668
    if ::pancurses::has_colors() {

715
        color: 0,
752
        color: 0,
716
        bold: false,
753
        bold: false,
717
        character: 0,
754
        character: 0,
718
    };
755
    };
719
    let mut i: libc::c_int = 0i32;
756
    let mut i: libc::c_int = 0i32;
720
    screen = unsafe {
757
    S.screen = unsafe {
721
        ::c2rust_runtime::CArray::alloc(
758
        ::c2rust_runtime::CArray::alloc(
722
            (::std::mem::size_of::<*mut libc::c_int>() as libc::c_ulong)
759
            (::std::mem::size_of::<*mut libc::c_int>() as libc::c_ulong)
723
                .wrapping_mul((win.as_ref().unwrap().get_max_x() - 1i32 + 1i32) as libc::c_ulong)
760
                .wrapping_mul((S.win.as_ref().unwrap().get_max_x() - 1i32 + 1i32) as libc::c_ulong)
724
                as usize
761
                as usize
725
                / ::std::mem::size_of::<::c2rust_runtime::CArray<i32>>(),
762
                / ::std::mem::size_of::<::c2rust_runtime::CArray<i32>>(),
726
        )
763
        )
727
    };
764
    };
728
    i = 0i32;
765
    i = 0i32;
729
    while i < win.as_ref().unwrap().get_max_x() - 1i32 + 1i32 {
766
    while i < S.win.as_ref().unwrap().get_max_x() - 1i32 + 1i32 {
730
        let ref mut fresh0 = screen[i as isize as usize];
767
        let ref mut fresh0 = S.screen[i as isize as usize];
731
        *fresh0 = unsafe {
768
        *fresh0 = unsafe {
732
            ::c2rust_runtime::CArray::alloc(
769
            ::c2rust_runtime::CArray::alloc(
733
                (::std::mem::size_of::() as libc::c_ulong).wrapping_mul(
770
                (::std::mem::size_of::() as libc::c_ulong).wrapping_mul(
734
                    (win.as_ref().unwrap().get_max_y() - 1i32 + 1i32) as libc::c_ulong,
771
                    (S.win.as_ref().unwrap().get_max_y() - 1i32 + 1i32) as libc::c_ulong,
735
                ) as usize
772
                ) as usize
736
                    / ::std::mem::size_of::(),
773
                    / ::std::mem::size_of::(),
737
            )
774
            )
738
        };
775
        };
739
        i += 1
776
        i += 1

742
    empty.y = -1i32;
779
    empty.y = -1i32;
743
    empty.color = 0i32;
780
    empty.color = 0i32;
744
    empty.bold = 0 != 0i32;
781
    empty.bold = 0 != 0i32;
745
    empty.character = ' ' as i32 as libc::c_char;
782
    empty.character = ' ' as i32 as libc::c_char;
746
    counter = 0i32;
783
    counter = 0i32;
747
    while counter <= win.as_ref().unwrap().get_max_x() - 1i32 {
784
    while counter <= S.win.as_ref().unwrap().get_max_x() - 1i32 {
748
        counter2 = 0i32;
785
        counter2 = 0i32;
749
        while counter2 <= win.as_ref().unwrap().get_max_y() - 1i32 {
786
        while counter2 <= S.win.as_ref().unwrap().get_max_y() - 1i32 {
750
            screen[counter as isize as usize][counter2 as isize as usize] = -1i32;
787
            S.screen[counter as isize as usize][counter2 as isize as usize] = -1i32;
751
            counter2 += 1
788
            counter2 += 1
752
        }
789
        }
753
        counter += 1
790
        counter += 1
754
    }
791
    }
755
    counter = 0i32;
792
    counter = 0i32;
756
    while (counter as libc::c_ulong)
793
    while (counter as libc::c_ulong)
757
        < (::std::mem::size_of::<[*mut libc::c_char; 406]>() as libc::c_ulong)
794
        < (::std::mem::size_of::<[*mut libc::c_char; 406]>() as libc::c_ulong)
758
            .wrapping_div(::std::mem::size_of::<*mut libc::c_char>() as libc::c_ulong)
795
            .wrapping_div(::std::mem::size_of::<*mut libc::c_char>() as libc::c_ulong)
759
    {
796
    {
760
        used_messages[counter as usize] = 0i32;
797
        S.used_messages[counter as usize] = 0i32;
761
        bogus_messages[counter as usize] = 0i32;
798
        S.bogus_messages[counter as usize] = 0i32;
762
        bogus[counter as usize] = empty;
799
        S.bogus[counter as usize] = empty;
763
        counter += 1
800
        counter += 1
764
    }
801
    }
765
}
802
}
766
#[no_mangle]
803
767
pub static mut bogus: [screen_object; 406] = [screen_object {
768
    x: 0,
769
    y: 0,
770
    color: 0,
771
    bold: false,
772
    character: 0,
773
}; 406];
774
#[no_mangle]
775
pub static mut bogus_messages: [libc::c_int; 406] = [0; 406];
776
#[no_mangle]
777
pub static mut used_messages: [libc::c_int; 406] = [0; 406];
778
/* This array contains our internal representation of the screen. The
804
/* This array contains our internal representation of the screen. The
779
array is bigger than it needs to be, as we don't need to keep track
805
array is bigger than it needs to be, as we don't need to keep track
780
of the first few rows of the screen. But that requires making an
806
of the first few rows of the screen. But that requires making an
781
offset function and using that everywhere. So not right now. */
807
offset function and using that everywhere. So not right now. */
782
#[no_mangle]
808
783
pub static mut screen: ::c2rust_runtime::CArray<::c2rust_runtime::CArray> =
784
    unsafe { ::c2rust_runtime::CArray::empty() };
785
#[no_mangle]
809
#[no_mangle]
786
pub unsafe extern "C" fn initialize_robot() {
810
pub unsafe extern "C" fn initialize_robot() {
787
    robot.x = rand() % (win.as_ref().unwrap().get_max_x() - 1i32) + 1i32;
811
    S.robot.x = rand() % (S.win.as_ref().unwrap().get_max_x() - 1i32) + 1i32;
788
    robot.y = rand() % (win.as_ref().unwrap().get_max_y() - 1i32 - 3i32 + 1i32) + 3i32;
812
    S.robot.y = rand() % (S.win.as_ref().unwrap().get_max_y() - 1i32 - 3i32 + 1i32) + 3i32;
789
    robot.character = '#' as i32 as libc::c_char;
813
    S.robot.character = '#' as i32 as libc::c_char;
790
    robot.color = 0i32;
814
    S.robot.color = 0i32;
791
    robot.bold = 0 != 0i32;
815
    S.robot.bold = 0 != 0i32;
792
    screen[robot.x as isize as usize][robot.y as isize as usize] = 0i32;
816
    S.screen[S.robot.x as isize as usize][S.robot.y as isize as usize] = 0i32;
793
}
817
}
794
/*Global variables. Bite me, it's fun.*/
818
/*Global variables. Bite me, it's fun.*/
795
#[no_mangle]
819
796
pub static mut robot: screen_object = screen_object {
797
    x: 0,
798
    y: 0,
799
    color: 0,
800
    bold: false,
801
    character: 0,
802
};
803
#[no_mangle]
820
#[no_mangle]
804
pub unsafe extern "C" fn initialize_kitten() {
821
pub unsafe extern "C" fn initialize_kitten() {
805
    loop {
822
    loop {
806
        kitten.x = rand() % (win.as_ref().unwrap().get_max_x() - 1i32) + 1i32;
823
        S.kitten.x = rand() % (S.win.as_ref().unwrap().get_max_x() - 1i32) + 1i32;
807
        kitten.y = rand() % (win.as_ref().unwrap().get_max_y() - 1i32 - 3i32 + 1i32) + 3i32;
824
        S.kitten.y = rand() % (S.win.as_ref().unwrap().get_max_y() - 1i32 - 3i32 + 1i32) + 3i32;
808
        if !(screen[kitten.x as isize as usize][kitten.y as isize as usize] != -1i32) {
825
        if !(S.screen[S.kitten.x as isize as usize][S.kitten.y as isize as usize] != -1i32) {
809
            break;
826
            break;
810
        }
827
        }
811
    }
828
    }
812
    loop {
829
    loop {
813
        kitten.character = (rand() % (126i32 - '!' as i32 + 1i32) + '!' as i32) as libc::c_char;
830
        S.kitten.character = (rand() % (126i32 - '!' as i32 + 1i32) + '!' as i32) as libc::c_char;
814
        if !(0 == validchar(kitten.character)) {
831
        if !(0 == validchar(S.kitten.character)) {
815
            break;
832
            break;
816
        }
833
        }
817
    }
834
    }
818
    screen[kitten.x as isize as usize][kitten.y as isize as usize] = 1i32;
835
    S.screen[S.kitten.x as isize as usize][S.kitten.y as isize as usize] = 1i32;
819
    kitten.color = rand() % 6i32 + 1i32;
836
    S.kitten.color = rand() % 6i32 + 1i32;
820
    kitten.bold = 0 != if 0 != rand() % 2i32 { 1i32 } else { 0i32 };
837
    S.kitten.bold = 0 != if 0 != rand() % 2i32 { 1i32 } else { 0i32 };
821
}
822
#[no_mangle]
823
pub static mut kitten: screen_object = screen_object {
824
    x: 0,
825
    y: 0,
826
    color: 0,
827
    bold: false,
828
    character: 0,
829
};
838
}
839
830
/*Helper functions*/
840
/*Helper functions*/
831
#[no_mangle]
841
#[no_mangle]
832
pub unsafe extern "C" fn validchar(mut a: libc::c_char) -> libc::c_int {
842
pub unsafe extern "C" fn validchar(mut a: libc::c_char) -> libc::c_int {
833
    match a as libc::c_int {
843
    match a as libc::c_int {
834
        35 | 32 | 127 => return 0i32,
844
        35 | 32 | 127 => return 0i32,

839
#[no_mangle]
849
#[no_mangle]
840
pub unsafe extern "C" fn initialize_bogus() {
850
pub unsafe extern "C" fn initialize_bogus() {
841
    let mut counter: libc::c_int = 0;
851
    let mut counter: libc::c_int = 0;
842
    let mut index: libc::c_int = 0;
852
    let mut index: libc::c_int = 0;
843
    counter = 0i32;
853
    counter = 0i32;
844
    while counter < num_bogus {
854
    while counter < S.num_bogus {
845
        bogus[counter as usize].color = rand() % 6i32 + 1i32;
855
        S.bogus[counter as usize].color = rand() % 6i32 + 1i32;
846
        bogus[counter as usize].bold = 0 != if 0 != rand() % 2i32 { 1i32 } else { 0i32 };
856
        S.bogus[counter as usize].bold = 0 != if 0 != rand() % 2i32 { 1i32 } else { 0i32 };
847
        loop {
857
        loop {
848
            bogus[counter as usize].character =
858
            S.bogus[counter as usize].character =
849
                (rand() % (126i32 - '!' as i32 + 1i32) + '!' as i32) as libc::c_char;
859
                (rand() % (126i32 - '!' as i32 + 1i32) + '!' as i32) as libc::c_char;
850
            if !(0 == validchar(bogus[counter as usize].character)) {
860
            if !(0 == validchar(S.bogus[counter as usize].character)) {
851
                break;
861
                break;
852
            }
862
            }
853
        }
863
        }
854
        loop {
864
        loop {
865
            S.bogus[counter as usize].x =
855
            bogus[counter as usize].x = rand() % (win.as_ref().unwrap().get_max_x() - 1i32) + 1i32;
866
                rand() % (S.win.as_ref().unwrap().get_max_x() - 1i32) + 1i32;
856
            bogus[counter as usize].y =
867
            S.bogus[counter as usize].y =
857
                rand() % (win.as_ref().unwrap().get_max_y() - 1i32 - 3i32 + 1i32) + 3i32;
868
                rand() % (S.win.as_ref().unwrap().get_max_y() - 1i32 - 3i32 + 1i32) + 3i32;
858
            if !(screen[bogus[counter as usize].x as isize as usize]
869
            if !(S.screen[S.bogus[counter as usize].x as isize as usize]
859
                [bogus[counter as usize].y as isize as usize]
870
                [S.bogus[counter as usize].y as isize as usize]
860
                != -1i32)
871
                != -1i32)
861
            {
872
            {
862
                break;
873
                break;
863
            }
874
            }
864
        }
875
        }
865
        screen[bogus[counter as usize].x as isize as usize]
876
        S.screen[S.bogus[counter as usize].x as isize as usize]
866
            [bogus[counter as usize].y as isize as usize] = counter + 2i32;
877
            [S.bogus[counter as usize].y as isize as usize] = counter + 2i32;
867
        loop {
878
        loop {
868
            index = (rand() as libc::c_ulong).wrapping_rem(
879
            index = (rand() as libc::c_ulong).wrapping_rem(
869
                (::std::mem::size_of::<[*mut libc::c_char; 406]>() as libc::c_ulong)
880
                (::std::mem::size_of::<[*mut libc::c_char; 406]>() as libc::c_ulong)
870
                    .wrapping_div(::std::mem::size_of::<*mut libc::c_char>() as libc::c_ulong),
881
                    .wrapping_div(::std::mem::size_of::<*mut libc::c_char>() as libc::c_ulong),
871
            ) as libc::c_int;
882
            ) as libc::c_int;
872
            if !(used_messages[index as usize] != 0i32) {
883
            if !(S.used_messages[index as usize] != 0i32) {
873
                break;
884
                break;
874
            }
885
            }
875
        }
886
        }
876
        bogus_messages[counter as usize] = index;
887
        S.bogus_messages[counter as usize] = index;
877
        used_messages[index as usize] = 1i32;
888
        S.used_messages[index as usize] = 1i32;
878
        counter += 1
889
        counter += 1
879
    }
890
    }
880
}
891
}
881
#[no_mangle]
892
882
pub static mut num_bogus: libc::c_int = 0;
883
#[no_mangle]
893
#[no_mangle]
884
pub unsafe extern "C" fn initialize_screen() {
894
pub unsafe extern "C" fn initialize_screen() {
885
    let mut counter: libc::c_int = 0;
895
    let mut counter: libc::c_int = 0;
886
    fmt_mvprintw(
896
    fmt_mvprintw(
887
        0i32,
897
        0i32,

891
                .to_str()
901
                .to_str()
892
                .unwrap()
902
                .unwrap()
893
        }),
903
        }),
894
    );
904
    );
895
    counter = 0i32;
905
    counter = 0i32;
896
    while counter <= win.as_ref().unwrap().get_max_x() - 1i32 {
906
    while counter <= S.win.as_ref().unwrap().get_max_x() - 1i32 {
897
        fmt_printw(format_args!("{:}", 95i32 as u8 as char));
907
        fmt_printw(format_args!("{:}", 95i32 as u8 as char));
898
        counter += 1
908
        counter += 1
899
    }
909
    }
900
    counter = 0i32;
910
    counter = 0i32;
901
    while counter < num_bogus {
911
    while counter < S.num_bogus {
902
        draw(bogus[counter as usize]);
912
        draw(S.bogus[counter as usize]);
903
        counter += 1
913
        counter += 1
904
    }
914
    }
905
    draw(kitten);
915
    draw(S.kitten);
906
    draw(robot);
916
    draw(S.robot);
907
    win.as_ref().unwrap().refresh();
917
    S.win.as_ref().unwrap().refresh();
908
}
918
}
909
#[no_mangle]
919
#[no_mangle]
910
pub unsafe extern "C" fn draw(mut o: screen_object) {
920
pub unsafe extern "C" fn draw(mut o: screen_object) {
911
    full_draw(o, 0 != 0i32);
921
    full_draw(o, 0 != 0i32);
912
}
922
}

935
        new |= 1u64 << 14i32 + 8i32
945
        new |= 1u64 << 14i32 + 8i32
936
    }
946
    }
937
    if o.bold {
947
    if o.bold {
938
        new |= 1u64 << 13i32 + 8i32
948
        new |= 1u64 << 13i32 + 8i32
939
    }
949
    }
950
    S.win
940
    win.as_ref()
951
        .as_ref()
941
        .unwrap()
952
        .unwrap()
942
        .attrset(new as libc::c_int as ::pancurses::chtype);
953
        .attrset(new as libc::c_int as ::pancurses::chtype);
943
    if in_place {
954
    if in_place {
944
        fmt_printw(format_args!(
955
        fmt_printw(format_args!(
945
            "{:}",
956
            "{:}",

949
        fmt_mvprintw(
960
        fmt_mvprintw(
950
            o.y,
961
            o.y,
951
            o.x,
962
            o.x,
952
            format_args!("{:}", o.character as libc::c_int as u8 as char),
963
            format_args!("{:}", o.character as libc::c_int as u8 as char),
953
        );
964
        );
954
        win.as_ref().unwrap().mv(o.y, o.x);
965
        S.win.as_ref().unwrap().mv(o.y, o.x);
955
    }
966
    }
967
    S.win
956
    win.as_ref()
968
        .as_ref()
957
        .unwrap()
969
        .unwrap()
958
        .attrset(old as libc::c_int as ::pancurses::chtype);
970
        .attrset(old as libc::c_int as ::pancurses::chtype);
959
}
971
}
960
#[no_mangle]
972
#[no_mangle]
961
pub unsafe extern "C" fn instructions() {
973
pub unsafe extern "C" fn instructions() {

974
    ));
986
    ));
975
    fmt_printw(format_args!(
987
    fmt_printw(format_args!(
976
        "Written originally for the Nerth Pork robotfindskitten contest\n\n"
988
        "Written originally for the Nerth Pork robotfindskitten contest\n\n"
977
    ));
989
    ));
978
    fmt_printw(format_args!("In this game, you are robot ("));
990
    fmt_printw(format_args!("In this game, you are robot ("));
979
    draw_in_place(robot);
991
    draw_in_place(S.robot);
980
    fmt_printw(format_args!("). Your job is to find kitten. This task\n"));
992
    fmt_printw(format_args!("). Your job is to find kitten. This task\n"));
981
    fmt_printw(format_args!(
993
    fmt_printw(format_args!(
982
        "is complicated by the existence of various things which are not kitten.\n"
994
        "is complicated by the existence of various things which are not kitten.\n"
983
    ));
995
    ));
984
    fmt_printw(format_args!(
996
    fmt_printw(format_args!(

989
    ));
1001
    ));
990
    fmt_printw(format_args!(
1002
    fmt_printw(format_args!(
991
        "the Esc key. See the documentation for more information.\n\n"
1003
        "the Esc key. See the documentation for more information.\n\n"
992
    ));
1004
    ));
993
    fmt_printw(format_args!("Press any key to start.\n"));
1005
    fmt_printw(format_args!("Press any key to start.\n"));
994
    win.as_ref().unwrap().refresh();
1006
    S.win.as_ref().unwrap().refresh();
995
    dummy = ::encode_input(win.as_ref().unwrap().getch()) as libc::c_char;
1007
    dummy = ::encode_input(S.win.as_ref().unwrap().getch()) as libc::c_char;
996
    win.as_ref().unwrap().clear();
1008
    S.win.as_ref().unwrap().clear();
997
}
1009
}
998
#[no_mangle]
1010
#[no_mangle]
999
pub unsafe extern "C" fn draw_in_place(mut o: screen_object) {
1011
pub unsafe extern "C" fn draw_in_place(mut o: screen_object) {
1000
    full_draw(o, 0 != 1i32);
1012
    full_draw(o, 0 != 1i32);
1001
}
1013
}
1002
/*Game functions*/
1014
/*Game functions*/
1003
#[no_mangle]
1015
#[no_mangle]
1004
pub unsafe extern "C" fn play_game() {
1016
pub unsafe extern "C" fn play_game() {
1005
    let mut old_x: libc::c_int = robot.x;
1017
    let mut old_x: libc::c_int = S.robot.x;
1006
    let mut old_y: libc::c_int = robot.y;
1018
    let mut old_y: libc::c_int = S.robot.y;
1007
    let mut input: libc::c_int = 0;
1019
    let mut input: libc::c_int = 0;
1008
    input = ::encode_input(win.as_ref().unwrap().getch());
1020
    input = ::encode_input(S.win.as_ref().unwrap().getch());
1009
    while input != 27i32 && input != 'q' as i32 && input != 'Q' as i32 {
1021
    while input != 27i32 && input != 'q' as i32 && input != 'Q' as i32 {
1010
        process_input(input);
1022
        process_input(input);
1011
        if !(old_x == robot.x && old_y == robot.y) {
1023
        if !(old_x == S.robot.x && old_y == S.robot.y) {
1012
            if win.as_ref().unwrap().mv(old_y, old_x) == -1i32 {
1024
            if S.win.as_ref().unwrap().mv(old_y, old_x) == -1i32 {
1013
            } else {
1025
            } else {
1014
                win.as_ref().unwrap().addch(' ' as i32 as chtype);
1026
                S.win.as_ref().unwrap().addch(' ' as i32 as chtype);
1015
            };
1027
            };
1016
            screen[old_x as isize as usize][old_y as isize as usize] = -1i32;
1028
            S.screen[old_x as isize as usize][old_y as isize as usize] = -1i32;
1017
            draw(robot);
1029
            draw(S.robot);
1018
            win.as_ref().unwrap().refresh();
1030
            S.win.as_ref().unwrap().refresh();
1019
            screen[robot.x as isize as usize][robot.y as isize as usize] = 0i32;
1031
            S.screen[S.robot.x as isize as usize][S.robot.y as isize as usize] = 0i32;
1020
            old_x = robot.x;
1032
            old_x = S.robot.x;
1021
            old_y = robot.y
1033
            old_y = S.robot.y
1022
        }
1034
        }
1023
        input = ::encode_input(win.as_ref().unwrap().getch())
1035
        input = ::encode_input(S.win.as_ref().unwrap().getch())
1024
    }
1036
    }
1025
    message(b"Bye!\x00" as *const u8 as *const libc::c_char as *mut libc::c_char);
1037
    message(b"Bye!\x00" as *const u8 as *const libc::c_char as *mut libc::c_char);
1026
    win.as_ref().unwrap().refresh();
1038
    S.win.as_ref().unwrap().refresh();
1027
    finish(0i32);
1039
    finish(0i32);
1028
}
1040
}
1029
#[no_mangle]
1041
#[no_mangle]
1030
pub unsafe extern "C" fn message(mut message_0: *mut libc::c_char) {
1042
pub unsafe extern "C" fn message(mut message_0: *mut libc::c_char) {
1031
    win.as_ref().unwrap().mv(1i32, 0i32);
1043
    S.win.as_ref().unwrap().mv(1i32, 0i32);
1032
    win.as_ref().unwrap().clrtoeol();
1044
    S.win.as_ref().unwrap().clrtoeol();
1033
    fmt_mvprintw(
1045
    fmt_mvprintw(
1034
        1i32,
1046
        1i32,
1035
        0i32,
1047
        0i32,
1036
        format_args!(
1048
        format_args!(
1037
            "{:.*}",
1049
            "{:.*}",
1038
            win.as_ref().unwrap().get_max_x() as usize,
1050
            S.win.as_ref().unwrap().get_max_x() as usize,
1039
            unsafe {
1051
            unsafe {
1040
                ::std::ffi::CStr::from_ptr(message_0 as *const libc::c_char)
1052
                ::std::ffi::CStr::from_ptr(message_0 as *const libc::c_char)
1041
                    .to_str()
1053
                    .to_str()
1042
                    .unwrap()
1054
                    .unwrap()
1043
            }
1055
            }
1044
        ),
1056
        ),
1045
    );
1057
    );
1046
    win.as_ref().unwrap().mv(robot.y, robot.x);
1058
    S.win.as_ref().unwrap().mv(S.robot.y, S.robot.x);
1047
    win.as_ref().unwrap().refresh();
1059
    S.win.as_ref().unwrap().refresh();
1048
}
1060
}
1049
#[no_mangle]
1061
#[no_mangle]
1050
pub unsafe extern "C" fn process_input(mut input: libc::c_int) {
1062
pub unsafe extern "C" fn process_input(mut input: libc::c_int) {
1051
    let mut check_x: libc::c_int = robot.x;
1063
    let mut check_x: libc::c_int = S.robot.x;
1052
    let mut check_y: libc::c_int = robot.y;
1064
    let mut check_y: libc::c_int = S.robot.y;
1053
    match input {
1065
    match input {
1054
        12 => {
1066
        12 => {
1055
            win.as_ref().unwrap().refresh();
1067
            S.win.as_ref().unwrap().refresh();
1056
        }
1068
        }
1057
        259 | 107 | 75 | 16 => check_y -= 1,
1069
        259 | 107 | 75 | 16 => check_y -= 1,
1058
        262 | 121 | 89 => {
1070
        262 | 121 | 89 => {
1059
            check_x -= 1;
1071
            check_x -= 1;
1060
            check_y -= 1
1072
            check_y -= 1

1082
            );
1094
            );
1083
            return;
1095
            return;
1084
        }
1096
        }
1085
    }
1097
    }
1086
    if check_y < 3i32
1098
    if check_y < 3i32
1087
        || check_y > win.as_ref().unwrap().get_max_y() - 1i32
1099
        || check_y > S.win.as_ref().unwrap().get_max_y() - 1i32
1088
        || check_x < 0i32
1100
        || check_x < 0i32
1089
        || check_x > win.as_ref().unwrap().get_max_x() - 1i32
1101
        || check_x > S.win.as_ref().unwrap().get_max_x() - 1i32
1090
    {
1102
    {
1091
        return;
1103
        return;
1092
    }
1104
    }
1093
    if screen[check_x as isize as usize][check_y as isize as usize] != -1i32 {
1105
    if S.screen[check_x as isize as usize][check_y as isize as usize] != -1i32 {
1094
        match screen[check_x as isize as usize][check_y as isize as usize] {
1106
        match S.screen[check_x as isize as usize][check_y as isize as usize] {
1095
            0 => {}
1107
            0 => {}
1096
            1 => {
1108
            1 => {
1097
                /*We didn't move, or we're stuck in a
1109
                /*We didn't move, or we're stuck in a
1098
                time warp or something.*/
1110
                time warp or something.*/
1099
                win.as_ref().unwrap().mv(1i32, 0i32);
1111
                S.win.as_ref().unwrap().mv(1i32, 0i32);
1100
                win.as_ref().unwrap().clrtoeol();
1112
                S.win.as_ref().unwrap().clrtoeol();
1101
                play_animation(input);
1113
                play_animation(input);
1102
            }
1114
            }
1103
            _ => {
1115
            _ => {
1104
                message(
1116
                message(
1105
                    messages[bogus_messages[(screen[check_x as isize as usize]
1117
                    messages[S.bogus_messages[(S.screen[check_x as isize as usize]
1106
                        [check_y as isize as usize]
1118
                        [check_y as isize as usize]
1107
                        - 2i32) as usize] as usize]
1119
                        - 2i32) as usize] as usize]
1108
                        .as_ptr() as *mut i8,
1120
                        .as_ptr() as *mut i8,
1109
                );
1121
                );
1110
            }
1122
            }
1111
        }
1123
        }
1112
        return;
1124
        return;
1113
    }
1125
    }
1114
    robot.x = check_x;
1126
    S.robot.x = check_x;
1115
    robot.y = check_y;
1127
    S.robot.y = check_y;
1116
}
1128
}
1117
#[no_mangle]
1129
#[no_mangle]
1118
pub unsafe extern "C" fn play_animation(mut input: libc::c_int) {
1130
pub unsafe extern "C" fn play_animation(mut input: libc::c_int) {
1119
    let mut counter: libc::c_int = 0;
1131
    let mut counter: libc::c_int = 0;
1120
    counter = 4i32;
1132
    counter = 4i32;
1121
    while counter > 0i32 {
1133
    while counter > 0i32 {
1122
        if win.as_ref().unwrap().mv(1i32, 50i32 + counter + 1i32) == -1i32 {
1134
        if S.win.as_ref().unwrap().mv(1i32, 50i32 + counter + 1i32) == -1i32 {
1123
        } else {
1135
        } else {
1124
            win.as_ref().unwrap().addch(' ' as i32 as chtype);
1136
            S.win.as_ref().unwrap().addch(' ' as i32 as chtype);
1125
        };
1137
        };
1126
        win.as_ref().unwrap().mv(1i32, 50i32 + counter);
1138
        S.win.as_ref().unwrap().mv(1i32, 50i32 + counter);
1127
        if input == 0o405i32 || input == 0o402i32 || input == 0o540i32 || input == 0o535i32 {
1139
        if input == 0o405i32 || input == 0o402i32 || input == 0o540i32 || input == 0o535i32 {
1128
            draw_in_place(kitten);
1140
            draw_in_place(S.kitten);
1129
        } else {
1141
        } else {
1130
            draw_in_place(robot);
1142
            draw_in_place(S.robot);
1131
        }
1143
        }
1132
        if win.as_ref().unwrap().mv(1i32, 50i32 - counter) == -1i32 {
1144
        if S.win.as_ref().unwrap().mv(1i32, 50i32 - counter) == -1i32 {
1133
        } else {
1145
        } else {
1134
            win.as_ref().unwrap().addch(' ' as i32 as chtype);
1146
            S.win.as_ref().unwrap().addch(' ' as i32 as chtype);
1135
        };
1147
        };
1136
        win.as_ref().unwrap().mv(1i32, 50i32 - counter + 1i32);
1148
        S.win.as_ref().unwrap().mv(1i32, 50i32 - counter + 1i32);
1137
        if input == 0o405i32 || input == 0o402i32 || input == 0o540i32 || input == 0o535i32 {
1149
        if input == 0o405i32 || input == 0o402i32 || input == 0o540i32 || input == 0o535i32 {
1138
            draw_in_place(robot);
1150
            draw_in_place(S.robot);
1139
        } else {
1151
        } else {
1140
            draw_in_place(kitten);
1152
            draw_in_place(S.kitten);
1141
        }
1153
        }
1142
        win.as_ref().unwrap().refresh();
1154
        S.win.as_ref().unwrap().refresh();
1143
        sleep(1i32 as libc::c_uint);
1155
        sleep(1i32 as libc::c_uint);
1144
        counter -= 1
1156
        counter -= 1
1145
    }
1157
    }
1146
    win.as_ref().unwrap().mv(1i32, 0i32);
1158
    S.win.as_ref().unwrap().mv(1i32, 0i32);
1147
    win.as_ref().unwrap().addnstr(
1159
    S.win.as_ref().unwrap().addnstr(
1148
        ::std::str::from_utf8(b"You found kitten! Way to go, robot!\x00")
1160
        ::std::str::from_utf8(b"You found kitten! Way to go, robot!\x00")
1149
            .unwrap()
1161
            .unwrap()
1150
            .trim_end_matches('\u{0}'),
1162
            .trim_end_matches('\u{0}'),
1151
        -1i32 as usize,
1163
        -1i32 as usize,
1152
    );
1164
    );
1153
    win.as_ref().unwrap().refresh();
1165
    S.win.as_ref().unwrap().refresh();
1154
    finish(0i32);
1166
    finish(0i32);
1155
}
1167
}
1156
unsafe fn main_0(mut argc: libc::c_int, mut argv: *mut *mut libc::c_char) -> libc::c_int {
1168
unsafe fn main_0(mut argc: libc::c_int, mut argv: *mut *mut libc::c_char) -> libc::c_int {
1157
    if argc == 1i32 {
1169
    if argc == 1i32 {
1158
        num_bogus = 20i32
1170
        S.num_bogus = 20i32
1159
    } else {
1171
    } else {
1160
        num_bogus = atoi(*argv.offset(1isize));
1172
        S.num_bogus = atoi(*argv.offset(1isize));
1161
        if num_bogus < 0i32
1173
        if S.num_bogus < 0i32
1162
            || num_bogus as libc::c_ulong
1174
            || S.num_bogus as libc::c_ulong
1163
                > (::std::mem::size_of::<[*mut libc::c_char; 406]>() as libc::c_ulong)
1175
                > (::std::mem::size_of::<[*mut libc::c_char; 406]>() as libc::c_ulong)
1164
                    .wrapping_div(::std::mem::size_of::<*mut libc::c_char>() as libc::c_ulong)
1176
                    .wrapping_div(::std::mem::size_of::<*mut libc::c_char>() as libc::c_ulong)
1165
        {
1177
        {
1166
            fmt_printf(format_args!(
1178
            fmt_printf(format_args!(
1167
                "Run-time parameter must be between 0 and {:}.\n",
1179
                "Run-time parameter must be between 0 and {:}.\n",

Then we run static_to_local_ref to pass a reference to the new State object everywhere it is used:

select target 'crate; child(static && name("S"));' ;
select user 'crate; desc(fn && !name("main|main_0"));' ;
static_to_local_ref ;

Diff #47

src/robotfindskitten.rs
17
    kitten: screen_object,
17
    kitten: screen_object,
18
    num_bogus: libc::c_int,
18
    num_bogus: libc::c_int,
19
}
19
}
20
static mut S: State = State {
20
static mut S: State = State {
21
    win: None,
21
    win: None,
22
    bogus: [screen_object {
22
    bogus: [screen_object {
23
        x: 0,
23
        x: 0,
24
        y: 0,
24
        y: 0,
25
        color: 0,
25
        color: 0,

50
extern crate pancurses;
50
extern crate pancurses;
51
extern "C" {
51
extern "C" {
52
    pub type ldat;
52
    pub type ldat;
53
    #[no_mangle]
53
    #[no_mangle]
54
    fn printf(_: *const libc::c_char, ...) -> libc::c_int;
54
    fn printf(_: *const libc::c_char, ...) -> libc::c_int;
55
    #[no_mangle]
55
    #[no_mangle]
56
    fn cbreak() -> libc::c_int;
56
    fn cbreak() -> libc::c_int;
57
    #[no_mangle]
57
    #[no_mangle]
58
    fn endwin() -> libc::c_int;
58
    fn endwin() -> libc::c_int;
59
    #[no_mangle]
59
    #[no_mangle]
60
    fn has_colors() -> bool;
60
    fn has_colors() -> bool;
61
    #[no_mangle]
61
    #[no_mangle]
62
    fn initscr() -> *mut WINDOW;
62
    fn initscr() -> *mut WINDOW;
63
    #[no_mangle]
63
    #[no_mangle]
64
    fn init_pair(_: libc::c_short, _: libc::c_short, _: libc::c_short) -> libc::c_int;
64
    fn init_pair(_: libc::c_short, _: libc::c_short, _: libc::c_short) -> libc::c_int;
65
    #[no_mangle]
65
    #[no_mangle]
66
    fn intrflush(_: *mut WINDOW, _: bool) -> libc::c_int;
66
    fn intrflush(_: *mut WINDOW, _: bool) -> libc::c_int;
67
    #[no_mangle]
67
    #[no_mangle]
68
    fn keypad(_: *mut WINDOW, _: bool) -> libc::c_int;
68
    fn keypad(_: *mut WINDOW, _: bool) -> libc::c_int;
69
    #[no_mangle]
69
    #[no_mangle]
70
    fn mvprintw(_: libc::c_int, _: libc::c_int, _: *const libc::c_char, ...) -> libc::c_int;
70
    fn mvprintw(_: libc::c_int, _: libc::c_int, _: *const libc::c_char, ...) -> libc::c_int;
71
    #[no_mangle]
71
    #[no_mangle]
72
    fn noecho() -> libc::c_int;
72
    fn noecho() -> libc::c_int;
73
    #[no_mangle]
73
    #[no_mangle]
74
    fn nonl() -> libc::c_int;
74
    fn nonl() -> libc::c_int;
75
    #[no_mangle]
75
    #[no_mangle]
76
    fn printw(_: *const libc::c_char, ...) -> libc::c_int;
76
    fn printw(_: *const libc::c_char, ...) -> libc::c_int;
77
    #[no_mangle]
77
    #[no_mangle]
78
    fn start_color() -> libc::c_int;
78
    fn start_color() -> libc::c_int;
79
    #[no_mangle]
79
    #[no_mangle]
80
    fn waddch(_: *mut WINDOW, _: chtype) -> libc::c_int;
80
    fn waddch(_: *mut WINDOW, _: chtype) -> libc::c_int;
81
    #[no_mangle]
81
    #[no_mangle]
82
    fn waddnstr(_: *mut WINDOW, _: *const libc::c_char, _: libc::c_int) -> libc::c_int;
82
    fn waddnstr(_: *mut WINDOW, _: *const libc::c_char, _: libc::c_int) -> libc::c_int;
83
    #[no_mangle]
83
    #[no_mangle]
84
    fn wclear(_: *mut WINDOW) -> libc::c_int;
84
    fn wclear(_: *mut WINDOW) -> libc::c_int;
85
    #[no_mangle]
85
    #[no_mangle]
86
    fn wclrtoeol(_: *mut WINDOW) -> libc::c_int;
86
    fn wclrtoeol(_: *mut WINDOW) -> libc::c_int;
87
    #[no_mangle]
87
    #[no_mangle]
88
    fn wgetch(_: *mut WINDOW) -> libc::c_int;
88
    fn wgetch(_: *mut WINDOW) -> libc::c_int;
89
    #[no_mangle]
89
    #[no_mangle]
90
    fn wmove(_: *mut WINDOW, _: libc::c_int, _: libc::c_int) -> libc::c_int;
90
    fn wmove(_: *mut WINDOW, _: libc::c_int, _: libc::c_int) -> libc::c_int;
91
    #[no_mangle]
91
    #[no_mangle]
92
    fn wrefresh(_: *mut WINDOW) -> libc::c_int;
92
    fn wrefresh(_: *mut WINDOW) -> libc::c_int;
93
    #[no_mangle]
93
    #[no_mangle]
94
    static mut curscr: *mut WINDOW;
94
    static mut curscr: *mut WINDOW;
95
    #[no_mangle]
95
    #[no_mangle]
96
    static mut stdscr: *mut WINDOW;
96
    static mut stdscr: *mut WINDOW;

98
    static mut COLS: libc::c_int;
98
    static mut COLS: libc::c_int;
99
    #[no_mangle]
99
    #[no_mangle]
100
    static mut LINES: libc::c_int;
100
    static mut LINES: libc::c_int;
101
    #[no_mangle]
101
    #[no_mangle]
102
    fn signal(__sig: libc::c_int, __handler: __sighandler_t) -> __sighandler_t;
102
    fn signal(__sig: libc::c_int, __handler: __sighandler_t) -> __sighandler_t;
103
    #[no_mangle]
103
    #[no_mangle]
104
    fn strtol(
104
    fn strtol(
105
        __nptr: *const libc::c_char,
105
        __nptr: *const libc::c_char,
106
        __endptr: *mut *mut libc::c_char,
106
        __endptr: *mut *mut libc::c_char,
107
        __base: libc::c_int,
107
        __base: libc::c_int,
108
    ) -> libc::c_long;
108
    ) -> libc::c_long;
109
    #[no_mangle]
109
    #[no_mangle]
110
    fn rand() -> libc::c_int;
110
    fn rand() -> libc::c_int;
111
    #[no_mangle]
111
    #[no_mangle]
112
    fn srand(__seed: libc::c_uint);
112
    fn srand(__seed: libc::c_uint);
113
    #[no_mangle]
113
    #[no_mangle]
114
    fn malloc(_: libc::c_ulong) -> *mut libc::c_void;
114
    fn malloc(_: libc::c_ulong) -> *mut libc::c_void;
115
    #[no_mangle]
115
    #[no_mangle]
116
    fn exit(_: libc::c_int) -> !;
116
    fn exit(_: libc::c_int) -> !;
117
    #[no_mangle]
117
    #[no_mangle]
118
    fn time(__timer: *mut time_t) -> time_t;
118
    fn time(__timer: *mut time_t) -> time_t;
119
    #[no_mangle]
119
    #[no_mangle]
120
    fn sleep(__seconds: libc::c_uint) -> libc::c_uint;
120
    fn sleep(__seconds: libc::c_uint) -> libc::c_uint;
121
}
121
}
122
extern "C" {
122
extern "C" {
123
    fn wattr_get(
123
    fn wattr_get(
124
        win: *mut WINDOW,
124
        win: *mut WINDOW,
125
        attrs: *mut attr_t,
125
        attrs: *mut attr_t,
126
        pair: *mut libc::c_short,
126
        pair: *mut libc::c_short,
127
        opts: *mut libc::c_void,
127
        opts: *mut libc::c_void,
128
    ) -> libc::c_int;
128
    ) -> libc::c_int;
129
    fn wattrset(win: *mut WINDOW, attrs: libc::c_int) -> libc::c_int;
129
    fn wattrset(win: *mut WINDOW, attrs: libc::c_int) -> libc::c_int;
130
}
130
}
131
fn fmt_mvprintw(y: libc::c_int, x: libc::c_int, args: ::std::fmt::Arguments) -> libc::c_int {
131
fn fmt_mvprintw(
132
    unsafe { S.win.as_ref().unwrap().mvprintw(y, x, &format!("{}", args)) }
132
    y: libc::c_int,
133
    x: libc::c_int,
134
    args: ::std::fmt::Arguments,
135
    S_: &mut State,
136
) -> libc::c_int {
137
    unsafe {
138
        (*S_)
139
            .win
140
            .as_ref()
141
            .unwrap()
142
            .mvprintw(y, x, &format!("{}", args))
143
    }
133
}
144
}
134
fn fmt_printw(args: ::std::fmt::Arguments) -> libc::c_int {
145
fn fmt_printw(args: ::std::fmt::Arguments, S_: &mut State) -> libc::c_int {
135
    unsafe { S.win.as_ref().unwrap().printw(&format!("{}", args)) }
146
    unsafe { (*S_).win.as_ref().unwrap().printw(&format!("{}", args)) }
136
}
147
}
137
fn fmt_printf(args: ::std::fmt::Arguments) -> libc::c_int {
148
fn fmt_printf(args: ::std::fmt::Arguments) -> libc::c_int {
138
    print!("{}", args);
149
    print!("{}", args);
139
    0
150
    0
140
}
151
}
141
pub type __time_t = libc::c_long;
152
pub type __time_t = libc::c_long;
142
pub type chtype = libc::c_ulong;
153
pub type chtype = libc::c_ulong;

230
    pub character: libc::c_char,
241
    pub character: libc::c_char,
231
}
242
}
232
static ver: &'static str = "1.7320508.406\u{0}";
243
static ver: &'static str = "1.7320508.406\u{0}";
233
#[inline]
244
#[inline]
234
unsafe extern "C" fn atoi(mut __nptr: *const libc::c_char) -> libc::c_int {
245
unsafe extern "C" fn atoi(mut __nptr: *const libc::c_char) -> libc::c_int {
235
    return strtol(
246
    return strtol(
236
        __nptr,
247
        __nptr,
237
        0 as *mut libc::c_void as *mut *mut libc::c_char,
248
        0 as *mut libc::c_void as *mut *mut libc::c_char,
238
        10i32,
249
        10i32,

655
/*
666
/*
656
 *Function definitions
667
 *Function definitions
657
 */
668
 */
658
/*Initialization and setup functions*/
669
/*Initialization and setup functions*/
659
#[no_mangle]
670
#[no_mangle]
660
pub unsafe extern "C" fn initialize_ncurses() {
671
pub unsafe extern "C" fn initialize_ncurses(S_: &mut State) {
661
    signal(2i32, Some(finish));
672
    signal(2i32, Some(finish));
662
    S.win = Some(::pancurses::initscr());
673
    (*S_).win = Some(::pancurses::initscr());
663
    S.win.as_ref().unwrap().keypad(0 != 1i32);
674
    (*S_).win.as_ref().unwrap().keypad(0 != 1i32);
664
    ::pancurses::nonl();
675
    ::pancurses::nonl();
665
    0;
676
    0;
666
    ::pancurses::noecho();
677
    ::pancurses::noecho();
667
    ::pancurses::cbreak();
678
    ::pancurses::cbreak();
668
    if ::pancurses::has_colors() {
679
    if ::pancurses::has_colors() {

709
        );
720
        );
710
    };
721
    };
711
}
722
}
712
fn encode_input(inp: Option<::pancurses::Input>) -> libc::c_int {
723
fn encode_input(inp: Option<::pancurses::Input>) -> libc::c_int {
713
    use pancurses::Input::*;
724
    use pancurses::Input::*;
714
    let inp = match inp {
725
    let inp = match inp {
715
        Some(x) => x,
726
        Some(x) => x,
716
        None => return -1,
727
        None => return -1,
717
    };
728
    };

734
        }
745
       }
735
    }
746
    }
736
}
747
}
737
unsafe extern "C" fn finish(mut sig: libc::c_int) {
748
unsafe extern "C" fn finish(mut sig: libc::c_int) {
738
    ::pancurses::endwin();
749
    ::pancurses::endwin();
739
    fmt_printf(format_args!(
750
    fmt_printf(format_args!(
740
        "{:}{:}{:}",
751
        "{:}{:}{:}",
741
        27i32 as u8 as char, '(' as i32 as u8 as char, 'B' as i32 as u8 as char
752
        27i32 as u8 as char, '(' as i32 as u8 as char, 'B' as i32 as u8 as char
742
    ));
753
    ));
743
    exit(0i32);
754
    exit(0i32);
744
}
755
}
745
#[no_mangle]
756
#[no_mangle]
746
pub unsafe extern "C" fn initialize_arrays() {
757
pub unsafe extern "C" fn initialize_arrays(S_: &mut State) {
747
    let mut counter: libc::c_int = 0;
758
    let mut counter: libc::c_int = 0;
748
    let mut counter2: libc::c_int = 0;
759
    let mut counter2: libc::c_int = 0;
749
    let mut empty: screen_object = screen_object {
760
    let mut empty: screen_object = screen_object {
750
        x: 0,
761
        x: 0,
751
        y: 0,
762
        y: 0,
752
        color: 0,
763
        color: 0,
753
        bold: false,
764
        bold: false,
754
        character: 0,
765
        character: 0,
755
    };
766
    };
756
    let mut i: libc::c_int = 0i32;
767
    let mut i: libc::c_int = 0i32;
757
    S.screen = unsafe {
768
    (*S_).screen = unsafe {
758
        ::c2rust_runtime::CArray::alloc(
769
        ::c2rust_runtime::CArray::alloc(
759
            (::std::mem::size_of::<*mut libc::c_int>() as libc::c_ulong)
770
            (::std::mem::size_of::<*mut libc::c_int>() as libc::c_ulong).wrapping_mul(
760
                .wrapping_mul((S.win.as_ref().unwrap().get_max_x() - 1i32 + 1i32) as libc::c_ulong)
771
                ((*S_).win.as_ref().unwrap().get_max_x() - 1i32 + 1i32) as libc::c_ulong,
761
                as usize
772
            ) as usize
762
                / ::std::mem::size_of::<::c2rust_runtime::CArray<i32>>(),
773
                / ::std::mem::size_of::<::c2rust_runtime::CArray<i32>>(),
763
        )
774
        )
764
    };
775
    };
765
    i = 0i32;
776
    i = 0i32;
766
    while i < S.win.as_ref().unwrap().get_max_x() - 1i32 + 1i32 {
777
    while i < (*S_).win.as_ref().unwrap().get_max_x() - 1i32 + 1i32 {
767
        let ref mut fresh0 = S.screen[i as isize as usize];
778
        let ref mut fresh0 = (*S_).screen[i as isize as usize];
768
        *fresh0 = unsafe {
779
        *fresh0 = unsafe {
769
            ::c2rust_runtime::CArray::alloc(
780
            ::c2rust_runtime::CArray::alloc(
770
                (::std::mem::size_of::() as libc::c_ulong).wrapping_mul(
781
                (::std::mem::size_of::() as libc::c_ulong).wrapping_mul(
771
                    (S.win.as_ref().unwrap().get_max_y() - 1i32 + 1i32) as libc::c_ulong,
782
                    ((*S_).win.as_ref().unwrap().get_max_y() - 1i32 + 1i32) as libc::c_ulong,
772
                ) as usize
783
                ) as usize
773
                    / ::std::mem::size_of::(),
784
                    / ::std::mem::size_of::(),
774
            )
785
            )
775
        };
786
        };
776
        i += 1
787
        i += 1

779
    empty.y = -1i32;
790
    empty.y = -1i32;
780
    empty.color = 0i32;
791
    empty.color = 0i32;
781
    empty.bold = 0 != 0i32;
792
    empty.bold = 0 != 0i32;
782
    empty.character = ' ' as i32 as libc::c_char;
793
    empty.character = ' ' as i32 as libc::c_char;
783
    counter = 0i32;
794
    counter = 0i32;
784
    while counter <= S.win.as_ref().unwrap().get_max_x() - 1i32 {
795
    while counter <= (*S_).win.as_ref().unwrap().get_max_x() - 1i32 {
785
        counter2 = 0i32;
796
        counter2 = 0i32;
786
        while counter2 <= S.win.as_ref().unwrap().get_max_y() - 1i32 {
797
        while counter2 <= (*S_).win.as_ref().unwrap().get_max_y() - 1i32 {
787
            S.screen[counter as isize as usize][counter2 as isize as usize] = -1i32;
798
            (*S_).screen[counter as isize as usize][counter2 as isize as usize] = -1i32;
788
            counter2 += 1
799
            counter2 += 1
789
        }
800
        }
790
        counter += 1
801
        counter += 1
791
    }
802
    }
792
    counter = 0i32;
803
    counter = 0i32;
793
    while (counter as libc::c_ulong)
804
    while (counter as libc::c_ulong)
794
        < (::std::mem::size_of::<[*mut libc::c_char; 406]>() as libc::c_ulong)
805
        < (::std::mem::size_of::<[*mut libc::c_char; 406]>() as libc::c_ulong)
795
            .wrapping_div(::std::mem::size_of::<*mut libc::c_char>() as libc::c_ulong)
806
            .wrapping_div(::std::mem::size_of::<*mut libc::c_char>() as libc::c_ulong)
796
    {
807
    {
797
        S.used_messages[counter as usize] = 0i32;
808
        (*S_).used_messages[counter as usize] = 0i32;
798
        S.bogus_messages[counter as usize] = 0i32;
809
        (*S_).bogus_messages[counter as usize] = 0i32;
799
        S.bogus[counter as usize] = empty;
810
        (*S_).bogus[counter as usize] = empty;
800
        counter += 1
811
        counter += 1
801
    }
812
    }
802
}
813
}
803
814
804
/* This array contains our internal representation of the screen. The
815
/* This array contains our internal representation of the screen. The
805
array is bigger than it needs to be, as we don't need to keep track
816
array is bigger than it needs to be, as we don't need to keep track
806
of the first few rows of the screen. But that requires making an
817
of the first few rows of the screen. But that requires making an
807
offset function and using that everywhere. So not right now. */
818
offset function and using that everywhere. So not right now. */
808
819
809
#[no_mangle]
820
#[no_mangle]
810
pub unsafe extern "C" fn initialize_robot() {
821
pub unsafe extern "C" fn initialize_robot(S_: &mut State) {
811
    S.robot.x = rand() % (S.win.as_ref().unwrap().get_max_x() - 1i32) + 1i32;
822
    (*S_).robot.x = rand() % ((*S_).win.as_ref().unwrap().get_max_x() - 1i32) + 1i32;
812
    S.robot.y = rand() % (S.win.as_ref().unwrap().get_max_y() - 1i32 - 3i32 + 1i32) + 3i32;
823
    (*S_).robot.y = rand() % ((*S_).win.as_ref().unwrap().get_max_y() - 1i32 - 3i32 + 1i32) + 3i32;
813
    S.robot.character = '#' as i32 as libc::c_char;
824
    (*S_).robot.character = '#' as i32 as libc::c_char;
814
    S.robot.color = 0i32;
825
    (*S_).robot.color = 0i32;
815
    S.robot.bold = 0 != 0i32;
826
    (*S_).robot.bold = 0 != 0i32;
816
    S.screen[S.robot.x as isize as usize][S.robot.y as isize as usize] = 0i32;
827
    (*S_).screen[(*S_).robot.x as isize as usize][(*S_).robot.y as isize as usize] = 0i32;
817
}
828
}
818
/*Global variables. Bite me, it's fun.*/
829
/*Global variables. Bite me, it's fun.*/
819
830
820
#[no_mangle]
831
#[no_mangle]
821
pub unsafe extern "C" fn initialize_kitten() {
832
pub unsafe extern "C" fn initialize_kitten(S_: &mut State) {
822
    loop {
833
    loop {
823
        S.kitten.x = rand() % (S.win.as_ref().unwrap().get_max_x() - 1i32) + 1i32;
834
        (*S_).kitten.x = rand() % ((*S_).win.as_ref().unwrap().get_max_x() - 1i32) + 1i32;
835
        (*S_).kitten.y =
824
        S.kitten.y = rand() % (S.win.as_ref().unwrap().get_max_y() - 1i32 - 3i32 + 1i32) + 3i32;
836
            rand() % ((*S_).win.as_ref().unwrap().get_max_y() - 1i32 - 3i32 + 1i32) + 3i32;
825
        if !(S.screen[S.kitten.x as isize as usize][S.kitten.y as isize as usize] != -1i32) {
837
        if !((*S_).screen[(*S_).kitten.x as isize as usize][(*S_).kitten.y as isize as usize]
838
            != -1i32)
839
        {
826
            break;
840
            break;
827
        }
841
        }
828
    }
842
    }
829
    loop {
843
    loop {
844
        (*S_).kitten.character =
830
        S.kitten.character = (rand() % (126i32 - '!' as i32 + 1i32) + '!' as i32) as libc::c_char;
845
            (rand() % (126i32 - '!' as i32 + 1i32) + '!' as i32) as libc::c_char;
831
        if !(0 == validchar(S.kitten.character)) {
846
        if !(0 == validchar((*S_).kitten.character)) {
832
            break;
847
            break;
833
        }
848
        }
834
    }
849
    }
835
    S.screen[S.kitten.x as isize as usize][S.kitten.y as isize as usize] = 1i32;
850
    (*S_).screen[(*S_).kitten.x as isize as usize][(*S_).kitten.y as isize as usize] = 1i32;
836
    S.kitten.color = rand() % 6i32 + 1i32;
851
    (*S_).kitten.color = rand() % 6i32 + 1i32;
837
    S.kitten.bold = 0 != if 0 != rand() % 2i32 { 1i32 } else { 0i32 };
852
    (*S_).kitten.bold = 0 != if 0 != rand() % 2i32 { 1i32 } else { 0i32 };
838
}
853
}
839
854
840
/*Helper functions*/
855
/*Helper functions*/
841
#[no_mangle]
856
#[no_mangle]
842
pub unsafe extern "C" fn validchar(mut a: libc::c_char) -> libc::c_int {
857
pub unsafe extern "C" fn validchar(mut a: libc::c_char) -> libc::c_int {
843
    match a as libc::c_int {
858
    match a as libc::c_int {
844
        35 | 32 | 127 => return 0i32,
859
        35 | 32 | 127 => return 0i32,
845
        _ => {}
860
        _ => {}
846
    }
861
    }
847
    return 1i32;
862
    return 1i32;
848
}
863
}
849
#[no_mangle]
864
#[no_mangle]
850
pub unsafe extern "C" fn initialize_bogus() {
865
pub unsafe extern "C" fn initialize_bogus(S_: &mut State) {
851
    let mut counter: libc::c_int = 0;
866
    let mut counter: libc::c_int = 0;
852
    let mut index: libc::c_int = 0;
867
    let mut index: libc::c_int = 0;
853
    counter = 0i32;
868
    counter = 0i32;
854
    while counter < S.num_bogus {
869
    while counter < (*S_).num_bogus {
855
        S.bogus[counter as usize].color = rand() % 6i32 + 1i32;
870
        (*S_).bogus[counter as usize].color = rand() % 6i32 + 1i32;
856
        S.bogus[counter as usize].bold = 0 != if 0 != rand() % 2i32 { 1i32 } else { 0i32 };
871
        (*S_).bogus[counter as usize].bold = 0 != if 0 != rand() % 2i32 { 1i32 } else { 0i32 };
857
        loop {
872
        loop {
858
            S.bogus[counter as usize].character =
873
            (*S_).bogus[counter as usize].character =
859
                (rand() % (126i32 - '!' as i32 + 1i32) + '!' as i32) as libc::c_char;
874
                (rand() % (126i32 - '!' as i32 + 1i32) + '!' as i32) as libc::c_char;
860
            if !(0 == validchar(S.bogus[counter as usize].character)) {
875
            if !(0 == validchar((*S_).bogus[counter as usize].character)) {
861
                break;
876
                break;
862
            }
877
            }
863
        }
878
        }
864
        loop {
879
        loop {
865
            S.bogus[counter as usize].x =
880
            (*S_).bogus[counter as usize].x =
866
                rand() % (S.win.as_ref().unwrap().get_max_x() - 1i32) + 1i32;
881
                rand() % ((*S_).win.as_ref().unwrap().get_max_x() - 1i32) + 1i32;
867
            S.bogus[counter as usize].y =
882
            (*S_).bogus[counter as usize].y =
868
                rand() % (S.win.as_ref().unwrap().get_max_y() - 1i32 - 3i32 + 1i32) + 3i32;
883
                rand() % ((*S_).win.as_ref().unwrap().get_max_y() - 1i32 - 3i32 + 1i32) + 3i32;
869
            if !(S.screen[S.bogus[counter as usize].x as isize as usize]
884
            if !((*S_).screen[(*S_).bogus[counter as usize].x as isize as usize]
870
                [S.bogus[counter as usize].y as isize as usize]
885
                [(*S_).bogus[counter as usize].y as isize as usize]
871
                != -1i32)
886
                != -1i32)
872
            {
887
            {
873
                break;
888
                break;
874
            }
889
            }
875
        }
890
        }
876
        S.screen[S.bogus[counter as usize].x as isize as usize]
891
        (*S_).screen[(*S_).bogus[counter as usize].x as isize as usize]
877
            [S.bogus[counter as usize].y as isize as usize] = counter + 2i32;
892
            [(*S_).bogus[counter as usize].y as isize as usize] = counter + 2i32;
878
        loop {
893
        loop {
879
            index = (rand() as libc::c_ulong).wrapping_rem(
894
            index = (rand() as libc::c_ulong).wrapping_rem(
880
                (::std::mem::size_of::<[*mut libc::c_char; 406]>() as libc::c_ulong)
895
                (::std::mem::size_of::<[*mut libc::c_char; 406]>() as libc::c_ulong)
881
                    .wrapping_div(::std::mem::size_of::<*mut libc::c_char>() as libc::c_ulong),
896
                    .wrapping_div(::std::mem::size_of::<*mut libc::c_char>() as libc::c_ulong),
882
            ) as libc::c_int;
897
            ) as libc::c_int;
883
            if !(S.used_messages[index as usize] != 0i32) {
898
            if !((*S_).used_messages[index as usize] != 0i32) {
884
                break;
899
                break;
885
            }
900
            }
886
        }
901
        }
887
        S.bogus_messages[counter as usize] = index;
902
        (*S_).bogus_messages[counter as usize] = index;
888
        S.used_messages[index as usize] = 1i32;
903
        (*S_).used_messages[index as usize] = 1i32;
889
        counter += 1
904
        counter += 1
890
    }
905
    }
891
}
906
}
892
907
893
#[no_mangle]
908
#[no_mangle]
894
pub unsafe extern "C" fn initialize_screen() {
909
pub unsafe extern "C" fn initialize_screen(S_: &mut State) {
895
    let mut counter: libc::c_int = 0;
910
    let mut counter: libc::c_int = 0;
896
    fmt_mvprintw(
911
    fmt_mvprintw(
897
        0i32,
912
        0i32,
898
        0i32,
913
        0i32,
899
        format_args!("robotfindskitten v{:}\n\n", unsafe {
914
        format_args!("robotfindskitten v{:}\n\n", unsafe {
900
            ::std::ffi::CStr::from_ptr(ver.as_ptr() as *const libc::c_char)
915
            ::std::ffi::CStr::from_ptr(ver.as_ptr() as *const libc::c_char)
901
                .to_str()
916
                .to_str()
902
                .unwrap()
917
                .unwrap()
903
        }),
918
        }),
919
        S_,
904
    );
920
    );
905
    counter = 0i32;
921
    counter = 0i32;
906
    while counter <= S.win.as_ref().unwrap().get_max_x() - 1i32 {
922
    while counter <= (*S_).win.as_ref().unwrap().get_max_x() - 1i32 {
907
        fmt_printw(format_args!("{:}", 95i32 as u8 as char));
923
        fmt_printw(format_args!("{:}", 95i32 as u8 as char), S_);
908
        counter += 1
924
        counter += 1
909
    }
925
    }
910
    counter = 0i32;
926
    counter = 0i32;
911
    while counter < S.num_bogus {
927
    while counter < (*S_).num_bogus {
912
        draw(S.bogus[counter as usize]);
928
        draw((*S_).bogus[counter as usize], S_);
913
        counter += 1
929
        counter += 1
914
    }
930
    }
915
    draw(S.kitten);
931
    draw((*S_).kitten, S_);
916
    draw(S.robot);
932
    draw((*S_).robot, S_);
917
    S.win.as_ref().unwrap().refresh();
933
    (*S_).win.as_ref().unwrap().refresh();
918
}
934
}
919
#[no_mangle]
935
#[no_mangle]
920
pub unsafe extern "C" fn draw(mut o: screen_object) {
936
pub unsafe extern "C" fn draw(mut o: screen_object, S_: &mut State) {
921
    full_draw(o, 0 != 0i32);
937
    full_draw(o, 0 != 0i32, S_);
922
}
938
}
923
#[no_mangle]
939
#[no_mangle]
924
pub unsafe extern "C" fn full_draw(mut o: screen_object, mut in_place: bool) {
940
pub unsafe extern "C" fn full_draw(mut o: screen_object, mut in_place: bool, S_: &mut State) {
925
    let mut old: attr_t = 0;
941
    let mut old: attr_t = 0;
926
    let mut dummy: libc::c_short = 0;
942
    let mut dummy: libc::c_short = 0;
927
    let mut new: attr_t = 0;
943
    let mut new: attr_t = 0;
928
    if !(stdscr as *const libc::c_void).is_null() {
944
    if !(stdscr as *const libc::c_void).is_null() {
929
        if !(&mut old as *mut attr_t as *const libc::c_void).is_null() {
945
        if !(&mut old as *mut attr_t as *const libc::c_void).is_null() {

945
        new |= 1u64 << 14i32 + 8i32
961
        new |= 1u64 << 14i32 + 8i32
946
    }
962
    }
947
    if o.bold {
963
    if o.bold {
948
        new |= 1u64 << 13i32 + 8i32
964
        new |= 1u64 << 13i32 + 8i32
949
    }
965
    }
966
    (*S_)
950
    S.win
967
        .win
951
        .as_ref()
968
        .as_ref()
952
        .unwrap()
969
        .unwrap()
953
        .attrset(new as libc::c_int as ::pancurses::chtype);
970
        .attrset(new as libc::c_int as ::pancurses::chtype);
954
    if in_place {
971
    if in_place {
955
        fmt_printw(format_args!(
972
        fmt_printw(
956
            "{:}",
957
            o.character as libc::c_int as u8 as char
973
            format_args!("{:}", o.character as libc::c_int as u8 as char),
974
            S_,
958
        ));
975
        );
959
    } else {
976
    } else {
960
        fmt_mvprintw(
977
        fmt_mvprintw(
961
            o.y,
978
            o.y,
962
            o.x,
979
            o.x,
963
            format_args!("{:}", o.character as libc::c_int as u8 as char),
980
            format_args!("{:}", o.character as libc::c_int as u8 as char),
981
            S_,
964
        );
982
        );
965
        S.win.as_ref().unwrap().mv(o.y, o.x);
983
        (*S_).win.as_ref().unwrap().mv(o.y, o.x);
966
    }
984
    }
985
    (*S_)
967
    S.win
986
        .win
968
        .as_ref()
987
        .as_ref()
969
        .unwrap()
988
        .unwrap()
970
        .attrset(old as libc::c_int as ::pancurses::chtype);
989
        .attrset(old as libc::c_int as ::pancurses::chtype);
971
}
990
}
972
#[no_mangle]
991
#[no_mangle]
973
pub unsafe extern "C" fn instructions() {
992
pub unsafe extern "C" fn instructions(S_: &mut State) {
974
    let mut dummy: libc::c_char = 0;
993
    let mut dummy: libc::c_char = 0;
975
    fmt_mvprintw(
994
    fmt_mvprintw(
976
        0i32,
995
        0i32,
977
        0i32,
996
        0i32,
978
        format_args!("robotfindskitten v{:}\n", unsafe {
997
        format_args!("robotfindskitten v{:}\n", unsafe {
979
            ::std::ffi::CStr::from_ptr(ver.as_ptr() as *const libc::c_char)
998
            ::std::ffi::CStr::from_ptr(ver.as_ptr() as *const libc::c_char)
980
                .to_str()
999
                .to_str()
981
                .unwrap()
1000
                .unwrap()
982
        }),
1001
        }),
1002
        S_,
983
    );
1003
    );
984
    fmt_printw(format_args!(
1004
    fmt_printw(
985
        "By the illustrious Leonard Richardson (C) 1997, 2000\n"
1005
        format_args!("By the illustrious Leonard Richardson (C) 1997, 2000\n"),
1006
        S_,
986
    ));
1007
    );
987
    fmt_printw(format_args!(
1008
    fmt_printw(
988
        "Written originally for the Nerth Pork robotfindskitten contest\n\n"
1009
        format_args!("Written originally for the Nerth Pork robotfindskitten contest\n\n"),
1010
        S_,
989
    ));
1011
    );
990
    fmt_printw(format_args!("In this game, you are robot ("));
1012
    fmt_printw(format_args!("In this game, you are robot ("), S_);
991
    draw_in_place(S.robot);
1013
    draw_in_place((*S_).robot, S_);
1014
    fmt_printw(
992
    fmt_printw(format_args!("). Your job is to find kitten. This task\n"));
1015
        format_args!("). Your job is to find kitten. This task\n"),
993
    fmt_printw(format_args!(
1016
        S_,
994
        "is complicated by the existence of various things which are not kitten.\n"
995
    ));
1017
    );
996
    fmt_printw(format_args!(
1018
    fmt_printw(
997
        "Robot must touch items to determine if they are kitten or not. The game\n"
1019
        format_args!("is complicated by the existence of various things which are not kitten.\n"),
1020
        S_,
998
    ));
1021
    );
999
    fmt_printw(format_args!(
1022
    fmt_printw(
1023
        format_args!("Robot must touch items to determine if they are kitten or not. The game\n"),
1024
        S_,
1025
    );
1026
    fmt_printw(
1027
        format_args!(
1000
        "ends when robotfindskitten. Alternatively, you may end the game by hitting\n"
1028
            "ends when robotfindskitten. Alternatively, you may end the game by hitting\n"
1029
        ),
1030
        S_,
1001
    ));
1031
    );
1002
    fmt_printw(format_args!(
1032
    fmt_printw(
1003
        "the Esc key. See the documentation for more information.\n\n"
1033
        format_args!("the Esc key. See the documentation for more information.\n\n"),
1034
        S_,
1004
    ));
1035
    );
1005
    fmt_printw(format_args!("Press any key to start.\n"));
1036
    fmt_printw(format_args!("Press any key to start.\n"), S_);
1006
    S.win.as_ref().unwrap().refresh();
1037
    (*S_).win.as_ref().unwrap().refresh();
1007
    dummy = ::encode_input(S.win.as_ref().unwrap().getch()) as libc::c_char;
1038
    dummy = ::encode_input((*S_).win.as_ref().unwrap().getch()) as libc::c_char;
1008
    S.win.as_ref().unwrap().clear();
1039
    (*S_).win.as_ref().unwrap().clear();
1009
}
1040
}
1010
#[no_mangle]
1041
#[no_mangle]
1011
pub unsafe extern "C" fn draw_in_place(mut o: screen_object) {
1042
pub unsafe extern "C" fn draw_in_place(mut o: screen_object, S_: &mut State) {
1012
    full_draw(o, 0 != 1i32);
1043
    full_draw(o, 0 != 1i32, S_);
1013
}
1044
}
1014
/*Game functions*/
1045
/*Game functions*/
1015
#[no_mangle]
1046
#[no_mangle]
1016
pub unsafe extern "C" fn play_game() {
1047
pub unsafe extern "C" fn play_game(S_: &mut State) {
1017
    let mut old_x: libc::c_int = S.robot.x;
1048
    let mut old_x: libc::c_int = (*S_).robot.x;
1018
    let mut old_y: libc::c_int = S.robot.y;
1049
    let mut old_y: libc::c_int = (*S_).robot.y;
1019
    let mut input: libc::c_int = 0;
1050
    let mut input: libc::c_int = 0;
1020
    input = ::encode_input(S.win.as_ref().unwrap().getch());
1051
    input = ::encode_input((*S_).win.as_ref().unwrap().getch());
1021
    while input != 27i32 && input != 'q' as i32 && input != 'Q' as i32 {
1052
    while input != 27i32 && input != 'q' as i32 && input != 'Q' as i32 {
1022
        process_input(input);
1053
        process_input(input, S_);
1023
        if !(old_x == S.robot.x && old_y == S.robot.y) {
1054
        if !(old_x == (*S_).robot.x && old_y == (*S_).robot.y) {
1024
            if S.win.as_ref().unwrap().mv(old_y, old_x) == -1i32 {
1055
            if (*S_).win.as_ref().unwrap().mv(old_y, old_x) == -1i32 {
1025
            } else {
1056
            } else {
1026
                S.win.as_ref().unwrap().addch(' ' as i32 as chtype);
1057
                (*S_).win.as_ref().unwrap().addch(' ' as i32 as chtype);
1027
            };
1058
            };
1028
            S.screen[old_x as isize as usize][old_y as isize as usize] = -1i32;
1059
            (*S_).screen[old_x as isize as usize][old_y as isize as usize] = -1i32;
1029
            draw(S.robot);
1060
            draw((*S_).robot, S_);
1030
            S.win.as_ref().unwrap().refresh();
1061
            (*S_).win.as_ref().unwrap().refresh();
1031
            S.screen[S.robot.x as isize as usize][S.robot.y as isize as usize] = 0i32;
1062
            (*S_).screen[(*S_).robot.x as isize as usize][(*S_).robot.y as isize as usize] = 0i32;
1032
            old_x = S.robot.x;
1063
            old_x = (*S_).robot.x;
1033
            old_y = S.robot.y
1064
            old_y = (*S_).robot.y
1034
        }
1065
        }
1035
        input = ::encode_input(S.win.as_ref().unwrap().getch())
1066
        input = ::encode_input((*S_).win.as_ref().unwrap().getch())
1036
    }
1067
    }
1068
    message(
1037
    message(b"Bye!\x00" as *const u8 as *const libc::c_char as *mut libc::c_char);
1069
        b"Bye!\x00" as *const u8 as *const libc::c_char as *mut libc::c_char,
1070
        S_,
1071
    );
1038
    S.win.as_ref().unwrap().refresh();
1072
    (*S_).win.as_ref().unwrap().refresh();
1039
    finish(0i32);
1073
    finish(0i32);
1040
}
1074
}
1041
#[no_mangle]
1075
#[no_mangle]
1042
pub unsafe extern "C" fn message(mut message_0: *mut libc::c_char) {
1076
pub unsafe extern "C" fn message(mut message_0: *mut libc::c_char, S_: &mut State) {
1043
    S.win.as_ref().unwrap().mv(1i32, 0i32);
1077
    (*S_).win.as_ref().unwrap().mv(1i32, 0i32);
1044
    S.win.as_ref().unwrap().clrtoeol();
1078
    (*S_).win.as_ref().unwrap().clrtoeol();
1045
    fmt_mvprintw(
1079
    fmt_mvprintw(
1046
        1i32,
1080
        1i32,
1047
        0i32,
1081
        0i32,
1048
        format_args!(
1082
        format_args!(
1049
            "{:.*}",
1083
            "{:.*}",
1050
            S.win.as_ref().unwrap().get_max_x() as usize,
1084
            (*S_).win.as_ref().unwrap().get_max_x() as usize,
1051
            unsafe {
1085
            unsafe {
1052
                ::std::ffi::CStr::from_ptr(message_0 as *const libc::c_char)
1086
                ::std::ffi::CStr::from_ptr(message_0 as *const libc::c_char)
1053
                    .to_str()
1087
                    .to_str()
1054
                    .unwrap()
1088
                    .unwrap()
1055
            }
1089
            }
1056
        ),
1090
        ),
1091
        S_,
1057
    );
1092
    );
1058
    S.win.as_ref().unwrap().mv(S.robot.y, S.robot.x);
1093
    (*S_).win.as_ref().unwrap().mv((*S_).robot.y, (*S_).robot.x);
1059
    S.win.as_ref().unwrap().refresh();
1094
    (*S_).win.as_ref().unwrap().refresh();
1060
}
1095
}
1061
#[no_mangle]
1096
#[no_mangle]
1062
pub unsafe extern "C" fn process_input(mut input: libc::c_int) {
1097
pub unsafe extern "C" fn process_input(mut input: libc::c_int, S_: &mut State) {
1063
    let mut check_x: libc::c_int = S.robot.x;
1098
    let mut check_x: libc::c_int = (*S_).robot.x;
1064
    let mut check_y: libc::c_int = S.robot.y;
1099
    let mut check_y: libc::c_int = (*S_).robot.y;
1065
    match input {
1100
    match input {
1066
        12 => {
1101
        12 => {
1067
            S.win.as_ref().unwrap().refresh();
1102
            (*S_).win.as_ref().unwrap().refresh();
1068
        }
1103
        }
1069
        259 | 107 | 75 | 16 => check_y -= 1,
1104
        259 | 107 | 75 | 16 => check_y -= 1,
1070
        262 | 121 | 89 => {
1105
        262 | 121 | 89 => {
1071
            check_x -= 1;
1106
            check_x -= 1;
1072
            check_y -= 1
1107
            check_y -= 1

1089
        0 => {}
1124
        0 => {}
1090
        _ => {
1125
        _ => {
1091
            message(
1126
            message(
1092
                b"Invalid input: Use direction keys or Esc.\x00" as *const u8 as *const libc::c_char
1127
                b"Invalid input: Use direction keys or Esc.\x00" as *const u8 as *const libc::c_char
1093
                    as *mut libc::c_char,
1128
                    as *mut libc::c_char,
1129
                S_,
1094
            );
1130
            );
1095
            return;
1131
            return;
1096
        }
1132
        }
1097
    }
1133
    }
1098
    if check_y < 3i32
1134
    if check_y < 3i32
1099
        || check_y > S.win.as_ref().unwrap().get_max_y() - 1i32
1135
        || check_y > (*S_).win.as_ref().unwrap().get_max_y() - 1i32
1100
        || check_x < 0i32
1136
        || check_x < 0i32
1101
        || check_x > S.win.as_ref().unwrap().get_max_x() - 1i32
1137
        || check_x > (*S_).win.as_ref().unwrap().get_max_x() - 1i32
1102
    {
1138
    {
1103
        return;
1139
        return;
1104
    }
1140
    }
1105
    if S.screen[check_x as isize as usize][check_y as isize as usize] != -1i32 {
1141
    if (*S_).screen[check_x as isize as usize][check_y as isize as usize] != -1i32 {
1106
        match S.screen[check_x as isize as usize][check_y as isize as usize] {
1142
        match (*S_).screen[check_x as isize as usize][check_y as isize as usize] {
1107
            0 => {}
1143
            0 => {}
1108
            1 => {
1144
            1 => {
1109
                /*We didn't move, or we're stuck in a
1145
                /*We didn't move, or we're stuck in a
1110
                time warp or something.*/
1146
                time warp or something.*/
1111
                S.win.as_ref().unwrap().mv(1i32, 0i32);
1147
                (*S_).win.as_ref().unwrap().mv(1i32, 0i32);
1112
                S.win.as_ref().unwrap().clrtoeol();
1148
                (*S_).win.as_ref().unwrap().clrtoeol();
1113
                play_animation(input);
1149
                play_animation(input, S_);
1114
            }
1150
            }
1115
            _ => {
1151
            _ => {
1116
                message(
1152
                message(
1117
                    messages[S.bogus_messages[(S.screen[check_x as isize as usize]
1153
                    messages[(*S_).bogus_messages[((*S_).screen[check_x as isize as usize]
1118
                        [check_y as isize as usize]
1154
                        [check_y as isize as usize]
1119
                        - 2i32) as usize] as usize]
1155
                        - 2i32) as usize] as usize]
1120
                        .as_ptr() as *mut i8,
1156
                        .as_ptr() as *mut i8,
1157
                    S_,
1121
                );
1158
                );
1122
            }
1159
            }
1123
        }
1160
        }
1124
        return;
1161
        return;
1125
    }
1162
    }
1126
    S.robot.x = check_x;
1163
    (*S_).robot.x = check_x;
1127
    S.robot.y = check_y;
1164
    (*S_).robot.y = check_y;
1128
}
1165
}
1129
#[no_mangle]
1166
#[no_mangle]
1130
pub unsafe extern "C" fn play_animation(mut input: libc::c_int) {
1167
pub unsafe extern "C" fn play_animation(mut input: libc::c_int, S_: &mut State) {
1131
    let mut counter: libc::c_int = 0;
1168
    let mut counter: libc::c_int = 0;
1132
    counter = 4i32;
1169
    counter = 4i32;
1133
    while counter > 0i32 {
1170
    while counter > 0i32 {
1134
        if S.win.as_ref().unwrap().mv(1i32, 50i32 + counter + 1i32) == -1i32 {
1171
        if (*S_).win.as_ref().unwrap().mv(1i32, 50i32 + counter + 1i32) == -1i32 {
1135
        } else {
1172
        } else {
1136
            S.win.as_ref().unwrap().addch(' ' as i32 as chtype);
1173
            (*S_).win.as_ref().unwrap().addch(' ' as i32 as chtype);
1137
        };
1174
        };
1138
        S.win.as_ref().unwrap().mv(1i32, 50i32 + counter);
1175
        (*S_).win.as_ref().unwrap().mv(1i32, 50i32 + counter);
1139
        if input == 0o405i32 || input == 0o402i32 || input == 0o540i32 || input == 0o535i32 {
1176
        if input == 0o405i32 || input == 0o402i32 || input == 0o540i32 || input == 0o535i32 {
1140
            draw_in_place(S.kitten);
1177
            draw_in_place((*S_).kitten, S_);
1141
        } else {
1178
        } else {
1142
            draw_in_place(S.robot);
1179
            draw_in_place((*S_).robot, S_);
1143
        }
1180
        }
1144
        if S.win.as_ref().unwrap().mv(1i32, 50i32 - counter) == -1i32 {
1181
        if (*S_).win.as_ref().unwrap().mv(1i32, 50i32 - counter) == -1i32 {
1145
        } else {
1182
        } else {
1146
            S.win.as_ref().unwrap().addch(' ' as i32 as chtype);
1183
            (*S_).win.as_ref().unwrap().addch(' ' as i32 as chtype);
1147
        };
1184
        };
1148
        S.win.as_ref().unwrap().mv(1i32, 50i32 - counter + 1i32);
1185
        (*S_).win.as_ref().unwrap().mv(1i32, 50i32 - counter + 1i32);
1149
        if input == 0o405i32 || input == 0o402i32 || input == 0o540i32 || input == 0o535i32 {
1186
        if input == 0o405i32 || input == 0o402i32 || input == 0o540i32 || input == 0o535i32 {
1150
            draw_in_place(S.robot);
1187
            draw_in_place((*S_).robot, S_);
1151
        } else {
1188
        } else {
1152
            draw_in_place(S.kitten);
1189
            draw_in_place((*S_).kitten, S_);
1153
        }
1190
        }
1154
        S.win.as_ref().unwrap().refresh();
1191
        (*S_).win.as_ref().unwrap().refresh();
1155
        sleep(1i32 as libc::c_uint);
1192
        sleep(1i32 as libc::c_uint);
1156
        counter -= 1
1193
        counter -= 1
1157
    }
1194
    }
1158
    S.win.as_ref().unwrap().mv(1i32, 0i32);
1195
    (*S_).win.as_ref().unwrap().mv(1i32, 0i32);
1159
    S.win.as_ref().unwrap().addnstr(
1196
    (*S_).win.as_ref().unwrap().addnstr(
1160
        ::std::str::from_utf8(b"You found kitten! Way to go, robot!\x00")
1197
        ::std::str::from_utf8(b"You found kitten! Way to go, robot!\x00")
1161
            .unwrap()
1198
            .unwrap()
1162
            .trim_end_matches('\u{0}'),
1199
            .trim_end_matches('\u{0}'),
1163
        -1i32 as usize,
1200
        -1i32 as usize,
1164
    );
1201
    );
1165
    S.win.as_ref().unwrap().refresh();
1202
    (*S_).win.as_ref().unwrap().refresh();
1166
    finish(0i32);
1203
    finish(0i32);
1167
}
1204
}
1168
unsafe fn main_0(mut argc: libc::c_int, mut argv: *mut *mut libc::c_char) -> libc::c_int {
1205
unsafe fn main_0(mut argc: libc::c_int, mut argv: *mut *mut libc::c_char) -> libc::c_int {
1169
    if argc == 1i32 {
1206
    if argc == 1i32 {
1170
        S.num_bogus = 20i32
1207
        S.num_bogus = 20i32

1187
    srand(time(0 as *mut time_t) as libc::c_uint);
1224
    srand(time(0 as *mut time_t) as libc::c_uint);
1188
    fmt_printf(format_args!(
1225
    fmt_printf(format_args!(
1189
        "{:}{:}{:}",
1226
        "{:}{:}{:}",
1190
        27i32 as u8 as char, '(' as i32 as u8 as char, 'U' as i32 as u8 as char
1227
        27i32 as u8 as char, '(' as i32 as u8 as char, 'U' as i32 as u8 as char
1191
    ));
1228
    ));
1192
    initialize_ncurses();
1229
    initialize_ncurses(&mut S);
1193
    initialize_arrays();
1230
    initialize_arrays(&mut S);
1194
    initialize_robot();
1231
    initialize_robot(&mut S);
1195
    initialize_kitten();
1232
    initialize_kitten(&mut S);
1196
    initialize_bogus();
1233
    initialize_bogus(&mut S);
1197
    instructions();
1234
    instructions(&mut S);
1198
    initialize_screen();
1235
    initialize_screen(&mut S);
1199
    play_game();
1236
    play_game(&mut S);
1200
    return 0;
1237
    return 0;
1201
}
1238
}
1202
pub fn main() {
1239
pub fn main() {
1203
    let mut args: Vec<*mut libc::c_char> = Vec::new();
1240
    let mut args: Vec<*mut libc::c_char> = Vec::new();
1204
    for arg in ::std::env::args() {
1241
    for arg in ::std::env::args() {

The functions that previously accessed the global S now use a reference argument S_, removing a source of unsafety.

The only function that still accesses S directly is main_0. And since main_0 is called only once per run of the program, we can replace the global S with a local variable declared inside main_0 without affecting the behavior of the program. The static_to_local command performs the necessary transformation (using the marks we previous set up for static_to_local_ref):

static_to_local

Diff #48

src/robotfindskitten.rs
15
    screen: ::c2rust_runtime::CArray<::c2rust_runtime::CArray>,
15
    screen: ::c2rust_runtime::CArray<::c2rust_runtime::CArray>,
16
    robot: screen_object,
16
    robot: screen_object,
17
    kitten: screen_object,
17
    kitten: screen_object,
18
    num_bogus: libc::c_int,
18
    num_bogus: libc::c_int,
19
}
19
}
20
static mut S: State = State {
21
    win: None,
22
    bogus: [screen_object {
23
        x: 0,
24
        y: 0,
25
        color: 0,
26
        bold: false,
27
        character: 0,
28
    }; 406],
29
    bogus_messages: [0; 406],
30
    used_messages: [0; 406],
31
    screen: unsafe { ::c2rust_runtime::CArray::empty() },
32
    robot: screen_object {
33
        x: 0,
34
        y: 0,
35
        color: 0,
36
        bold: false,
37
        character: 0,
38
    },
39
    kitten: screen_object {
40
        x: 0,
41
        y: 0,
42
        color: 0,
43
        bold: false,
44
        character: 0,
45
    },
46
    num_bogus: 0,
47
};
48
extern crate c2rust_runtime;
20
extern crate c2rust_runtime;
49
extern crate libc;
21
extern crate libc;
50
extern crate pancurses;
22
extern crate pancurses;
51
extern "C" {
23
extern "C" {
52
    pub type ldat;
24
    pub type ldat;

1201
    );
1173
    );
1202
    (*S_).win.as_ref().unwrap().refresh();
1174
    (*S_).win.as_ref().unwrap().refresh();
1203
    finish(0i32);
1175
    finish(0i32);
1204
}
1176
}
1205
unsafe fn main_0(mut argc: libc::c_int, mut argv: *mut *mut libc::c_char) -> libc::c_int {
1177
unsafe fn main_0(mut argc: libc::c_int, mut argv: *mut *mut libc::c_char) -> libc::c_int {
1178
    let mut S: State = State {
1179
        win: None,
1180
        bogus: [screen_object {
1181
            x: 0,
1182
            y: 0,
1183
            color: 0,
1184
            bold: false,
1185
            character: 0,
1186
        }; 406],
1187
        bogus_messages: [0; 406],
1188
        used_messages: [0; 406],
1189
        screen: unsafe { ::c2rust_runtime::CArray::empty() },
1190
        robot: screen_object {
1191
            x: 0,
1192
            y: 0,
1193
            color: 0,
1194
            bold: false,
1195
            character: 0,
1196
        },
1197
        kitten: screen_object {
1198
            x: 0,
1199
            y: 0,
1200
            color: 0,
1201
            bold: false,
1202
            character: 0,
1203
        },
1204
        num_bogus: 0,
1205
    };
1206
    if argc == 1i32 {
1206
    if argc == 1i32 {
1207
        S.num_bogus = 20i32
1207
        S.num_bogus = 20i32
1208
    } else {
1208
    } else {
1209
        S.num_bogus = atoi(*argv.offset(1isize));
1209
        S.num_bogus = atoi(*argv.offset(1isize));
1210
        if S.num_bogus < 0i32
1210
        if S.num_bogus < 0i32

Now there are no static muts remaining in the program.

There is one final cleanup step to perform. The struct State appears in the signature of several public functions, but State itself is not public, so rustc reports an error. We could make State public, but since there is no reason for the functions in question to be public in the first place, we make the functions private instead:

select target 'crate; desc(fn && !name("main"));' ;
set_visibility '' ;

commit

Diff #49

src/robotfindskitten.rs
22
extern crate pancurses;
22
extern crate pancurses;
23
extern "C" {
23
extern "C" {
24
    pub type ldat;
24
    pub type ldat;
25
    #[no_mangle]
25
    #[no_mangle]
26
    fn printf(_: *const libc::c_char, ...) -> libc::c_int;
26
    fn printf(_: *const libc::c_char, ...) -> libc::c_int;
27
    #[no_mangle]
27
    #[no_mangle]
28
    fn cbreak() -> libc::c_int;
28
    fn cbreak() -> libc::c_int;
29
    #[no_mangle]
29
    #[no_mangle]
30
    fn endwin() -> libc::c_int;
30
    fn endwin() -> libc::c_int;
31
    #[no_mangle]
31
    #[no_mangle]
32
    fn has_colors() -> bool;
32
    fn has_colors() -> bool;
33
    #[no_mangle]
33
    #[no_mangle]
34
    fn initscr() -> *mut WINDOW;
34
    fn initscr() -> *mut WINDOW;
35
    #[no_mangle]
35
    #[no_mangle]
36
    fn init_pair(_: libc::c_short, _: libc::c_short, _: libc::c_short) -> libc::c_int;
36
    fn init_pair(_: libc::c_short, _: libc::c_short, _: libc::c_short) -> libc::c_int;
37
    #[no_mangle]
37
    #[no_mangle]
38
    fn intrflush(_: *mut WINDOW, _: bool) -> libc::c_int;
38
    fn intrflush(_: *mut WINDOW, _: bool) -> libc::c_int;
39
    #[no_mangle]
39
    #[no_mangle]
40
    fn keypad(_: *mut WINDOW, _: bool) -> libc::c_int;
40
    fn keypad(_: *mut WINDOW, _: bool) -> libc::c_int;
41
    #[no_mangle]
41
    #[no_mangle]
42
    fn mvprintw(_: libc::c_int, _: libc::c_int, _: *const libc::c_char, ...) -> libc::c_int;
42
    fn mvprintw(_: libc::c_int, _: libc::c_int, _: *const libc::c_char, ...) -> libc::c_int;
43
    #[no_mangle]
43
    #[no_mangle]
44
    fn noecho() -> libc::c_int;
44
    fn noecho() -> libc::c_int;
45
    #[no_mangle]
45
    #[no_mangle]
46
    fn nonl() -> libc::c_int;
46
    fn nonl() -> libc::c_int;
47
    #[no_mangle]
47
    #[no_mangle]
48
    fn printw(_: *const libc::c_char, ...) -> libc::c_int;
48
    fn printw(_: *const libc::c_char, ...) -> libc::c_int;
49
    #[no_mangle]
49
    #[no_mangle]
50
    fn start_color() -> libc::c_int;
50
    fn start_color() -> libc::c_int;
51
    #[no_mangle]
51
    #[no_mangle]
52
    fn waddch(_: *mut WINDOW, _: chtype) -> libc::c_int;
52
    fn waddch(_: *mut WINDOW, _: chtype) -> libc::c_int;
53
    #[no_mangle]
53
    #[no_mangle]
54
    fn waddnstr(_: *mut WINDOW, _: *const libc::c_char, _: libc::c_int) -> libc::c_int;
54
    fn waddnstr(_: *mut WINDOW, _: *const libc::c_char, _: libc::c_int) -> libc::c_int;
55
    #[no_mangle]
55
    #[no_mangle]
56
    fn wclear(_: *mut WINDOW) -> libc::c_int;
56
    fn wclear(_: *mut WINDOW) -> libc::c_int;
57
    #[no_mangle]
57
    #[no_mangle]
58
    fn wclrtoeol(_: *mut WINDOW) -> libc::c_int;
58
    fn wclrtoeol(_: *mut WINDOW) -> libc::c_int;
59
    #[no_mangle]
59
    #[no_mangle]
60
    fn wgetch(_: *mut WINDOW) -> libc::c_int;
60
    fn wgetch(_: *mut WINDOW) -> libc::c_int;
61
    #[no_mangle]
61
    #[no_mangle]
62
    fn wmove(_: *mut WINDOW, _: libc::c_int, _: libc::c_int) -> libc::c_int;
62
    fn wmove(_: *mut WINDOW, _: libc::c_int, _: libc::c_int) -> libc::c_int;
63
    #[no_mangle]
63
    #[no_mangle]
64
    fn wrefresh(_: *mut WINDOW) -> libc::c_int;
64
    fn wrefresh(_: *mut WINDOW) -> libc::c_int;
65
    #[no_mangle]
65
    #[no_mangle]
66
    static mut curscr: *mut WINDOW;
66
    static mut curscr: *mut WINDOW;
67
    #[no_mangle]
67
    #[no_mangle]
68
    static mut stdscr: *mut WINDOW;
68
    static mut stdscr: *mut WINDOW;

70
    static mut COLS: libc::c_int;
70
    static mut COLS: libc::c_int;
71
    #[no_mangle]
71
    #[no_mangle]
72
    static mut LINES: libc::c_int;
72
    static mut LINES: libc::c_int;
73
    #[no_mangle]
73
    #[no_mangle]
74
    fn signal(__sig: libc::c_int, __handler: __sighandler_t) -> __sighandler_t;
74
    fn signal(__sig: libc::c_int, __handler: __sighandler_t) -> __sighandler_t;
75
    #[no_mangle]
75
    #[no_mangle]
76
    fn strtol(
76
    fn strtol(
77
        __nptr: *const libc::c_char,
77
        __nptr: *const libc::c_char,
78
        __endptr: *mut *mut libc::c_char,
78
        __endptr: *mut *mut libc::c_char,
79
        __base: libc::c_int,
79
        __base: libc::c_int,
80
    ) -> libc::c_long;
80
    ) -> libc::c_long;
81
    #[no_mangle]
81
    #[no_mangle]
82
    fn rand() -> libc::c_int;
82
    fn rand() -> libc::c_int;
83
    #[no_mangle]
83
    #[no_mangle]
84
    fn srand(__seed: libc::c_uint);
84
    fn srand(__seed: libc::c_uint);
85
    #[no_mangle]
85
    #[no_mangle]
86
    fn malloc(_: libc::c_ulong) -> *mut libc::c_void;
86
    fn malloc(_: libc::c_ulong) -> *mut libc::c_void;
87
    #[no_mangle]
87
    #[no_mangle]
88
    fn exit(_: libc::c_int) -> !;
88
    fn exit(_: libc::c_int) -> !;
89
    #[no_mangle]
89
    #[no_mangle]
90
    fn time(__timer: *mut time_t) -> time_t;
90
    fn time(__timer: *mut time_t) -> time_t;
91
    #[no_mangle]
91
    #[no_mangle]
92
    fn sleep(__seconds: libc::c_uint) -> libc::c_uint;
92
    fn sleep(__seconds: libc::c_uint) -> libc::c_uint;
93
}
93
}
94
extern "C" {
94
extern "C" {
95
    fn wattr_get(
95
    fn wattr_get(
96
        win: *mut WINDOW,
96
        win: *mut WINDOW,
97
        attrs: *mut attr_t,
97
        attrs: *mut attr_t,
98
        pair: *mut libc::c_short,
98
        pair: *mut libc::c_short,
99
        opts: *mut libc::c_void,
99
        opts: *mut libc::c_void,
100
    ) -> libc::c_int;
100
    ) -> libc::c_int;
101
    fn wattrset(win: *mut WINDOW, attrs: libc::c_int) -> libc::c_int;
101
    fn wattrset(win: *mut WINDOW, attrs: libc::c_int) -> libc::c_int;
102
}
102
}
103
fn fmt_mvprintw(
103
fn fmt_mvprintw(
104
    y: libc::c_int,
104
    y: libc::c_int,
105
    x: libc::c_int,
105
    x: libc::c_int,
106
    args: ::std::fmt::Arguments,
106
    args: ::std::fmt::Arguments,

117
fn fmt_printw(args: ::std::fmt::Arguments, S_: &mut State) -> libc::c_int {
117
fn fmt_printw(args: ::std::fmt::Arguments, S_: &mut State) -> libc::c_int {
118
    unsafe { (*S_).win.as_ref().unwrap().printw(&format!("{}", args)) }
118
    unsafe { (*S_).win.as_ref().unwrap().printw(&format!("{}", args)) }
119
}
119
}
120
fn fmt_printf(args: ::std::fmt::Arguments) -> libc::c_int {
120
fn fmt_printf(args: ::std::fmt::Arguments) -> libc::c_int {
121
    print!("{}", args);
121
    print!("{}", args);
122
    0
122
    0
123
}
123
}
124
pub type __time_t = libc::c_long;
124
pub type __time_t = libc::c_long;
125
pub type chtype = libc::c_ulong;
125
pub type chtype = libc::c_ulong;

213
    pub character: libc::c_char,
213
    pub character: libc::c_char,
214
}
214
}
215
static ver: &'static str = "1.7320508.406\u{0}";
215
static ver: &'static str = "1.7320508.406\u{0}";
216
#[inline]
216
#[inline]
217
unsafe extern "C" fn atoi(mut __nptr: *const libc::c_char) -> libc::c_int {
217
unsafe extern "C" fn atoi(mut __nptr: *const libc::c_char) -> libc::c_int {
218
    return strtol(
218
    return strtol(
219
        __nptr,
219
        __nptr,
220
        0 as *mut libc::c_void as *mut *mut libc::c_char,
220
        0 as *mut libc::c_void as *mut *mut libc::c_char,
221
        10i32,
221
        10i32,

638
/*
638
/*
639
 *Function definitions
639
 *Function definitions
640
 */
640
 */
641
/*Initialization and setup functions*/
641
/*Initialization and setup functions*/
642
#[no_mangle]
642
#[no_mangle]
643
pub unsafe extern "C" fn initialize_ncurses(S_: &mut State) {
643
unsafe extern "C" fn initialize_ncurses(S_: &mut State) {
644
    signal(2i32, Some(finish));
644
    signal(2i32, Some(finish));
645
    (*S_).win = Some(::pancurses::initscr());
645
    (*S_).win = Some(::pancurses::initscr());
646
    (*S_).win.as_ref().unwrap().keypad(0 != 1i32);
646
    (*S_).win.as_ref().unwrap().keypad(0 != 1i32);
647
    ::pancurses::nonl();
647
    ::pancurses::nonl();
648
    0;
648
    0;

692
        );
692
        );
693
    };
693
    };
694
}
694
}
695
fn encode_input(inp: Option<::pancurses::Input>) -> libc::c_int {
695
fn encode_input(inp: Option<::pancurses::Input>) -> libc::c_int {
696
    use pancurses::Input::*;
696
    use pancurses::Input::*;
697
    let inp = match inp {
697
    let inp = match inp {
698
        Some(x) => x,
698
        Some(x) => x,
699
        None => return -1,
699
        None => return -1,
700
    };
700
    };

717
       }
717
        }
718
    }
718
    }
719
}
719
}
720
unsafe extern "C" fn finish(mut sig: libc::c_int) {
720
unsafe extern "C" fn finish(mut sig: libc::c_int) {
721
    ::pancurses::endwin();
721
    ::pancurses::endwin();
722
    fmt_printf(format_args!(
722
    fmt_printf(format_args!(
723
        "{:}{:}{:}",
723
        "{:}{:}{:}",
724
        27i32 as u8 as char, '(' as i32 as u8 as char, 'B' as i32 as u8 as char
724
        27i32 as u8 as char, '(' as i32 as u8 as char, 'B' as i32 as u8 as char
725
    ));
725
    ));
726
    exit(0i32);
726
    exit(0i32);
727
}
727
}
728
#[no_mangle]
728
#[no_mangle]
729
pub unsafe extern "C" fn initialize_arrays(S_: &mut State) {
729
unsafe extern "C" fn initialize_arrays(S_: &mut State) {
730
    let mut counter: libc::c_int = 0;
730
    let mut counter: libc::c_int = 0;
731
    let mut counter2: libc::c_int = 0;
731
    let mut counter2: libc::c_int = 0;
732
    let mut empty: screen_object = screen_object {
732
    let mut empty: screen_object = screen_object {
733
        x: 0,
733
        x: 0,
734
        y: 0,
734
        y: 0,

788
array is bigger than it needs to be, as we don't need to keep track
788
array is bigger than it needs to be, as we don't need to keep track
789
of the first few rows of the screen. But that requires making an
789
of the first few rows of the screen. But that requires making an
790
offset function and using that everywhere. So not right now. */
790
offset function and using that everywhere. So not right now. */
791
791
792
#[no_mangle]
792
#[no_mangle]
793
pub unsafe extern "C" fn initialize_robot(S_: &mut State) {
793
unsafe extern "C" fn initialize_robot(S_: &mut State) {
794
    (*S_).robot.x = rand() % ((*S_).win.as_ref().unwrap().get_max_x() - 1i32) + 1i32;
794
    (*S_).robot.x = rand() % ((*S_).win.as_ref().unwrap().get_max_x() - 1i32) + 1i32;
795
    (*S_).robot.y = rand() % ((*S_).win.as_ref().unwrap().get_max_y() - 1i32 - 3i32 + 1i32) + 3i32;
795
    (*S_).robot.y = rand() % ((*S_).win.as_ref().unwrap().get_max_y() - 1i32 - 3i32 + 1i32) + 3i32;
796
    (*S_).robot.character = '#' as i32 as libc::c_char;
796
    (*S_).robot.character = '#' as i32 as libc::c_char;
797
    (*S_).robot.color = 0i32;
797
    (*S_).robot.color = 0i32;
798
    (*S_).robot.bold = 0 != 0i32;
798
    (*S_).robot.bold = 0 != 0i32;
799
    (*S_).screen[(*S_).robot.x as isize as usize][(*S_).robot.y as isize as usize] = 0i32;
799
    (*S_).screen[(*S_).robot.x as isize as usize][(*S_).robot.y as isize as usize] = 0i32;
800
}
800
}
801
/*Global variables. Bite me, it's fun.*/
801
/*Global variables. Bite me, it's fun.*/
802
802
803
#[no_mangle]
803
#[no_mangle]
804
pub unsafe extern "C" fn initialize_kitten(S_: &mut State) {
804
unsafe extern "C" fn initialize_kitten(S_: &mut State) {
805
    loop {
805
    loop {
806
        (*S_).kitten.x = rand() % ((*S_).win.as_ref().unwrap().get_max_x() - 1i32) + 1i32;
806
        (*S_).kitten.x = rand() % ((*S_).win.as_ref().unwrap().get_max_x() - 1i32) + 1i32;
807
        (*S_).kitten.y =
807
        (*S_).kitten.y =
808
            rand() % ((*S_).win.as_ref().unwrap().get_max_y() - 1i32 - 3i32 + 1i32) + 3i32;
808
            rand() % ((*S_).win.as_ref().unwrap().get_max_y() - 1i32 - 3i32 + 1i32) + 3i32;
809
        if !((*S_).screen[(*S_).kitten.x as isize as usize][(*S_).kitten.y as isize as usize]
809
        if !((*S_).screen[(*S_).kitten.x as isize as usize][(*S_).kitten.y as isize as usize]

824
    (*S_).kitten.bold = 0 != if 0 != rand() % 2i32 { 1i32 } else { 0i32 };
824
    (*S_).kitten.bold = 0 != if 0 != rand() % 2i32 { 1i32 } else { 0i32 };
825
}
825
}
826
826
827
/*Helper functions*/
827
/*Helper functions*/
828
#[no_mangle]
828
#[no_mangle]
829
pub unsafe extern "C" fn validchar(mut a: libc::c_char) -> libc::c_int {
829
unsafe extern "C" fn validchar(mut a: libc::c_char) -> libc::c_int {
830
    match a as libc::c_int {
830
    match a as libc::c_int {
831
        35 | 32 | 127 => return 0i32,
831
        35 | 32 | 127 => return 0i32,
832
        _ => {}
832
        _ => {}
833
    }
833
    }
834
    return 1i32;
834
    return 1i32;
835
}
835
}
836
#[no_mangle]
836
#[no_mangle]
837
pub unsafe extern "C" fn initialize_bogus(S_: &mut State) {
837
unsafe extern "C" fn initialize_bogus(S_: &mut State) {
838
    let mut counter: libc::c_int = 0;
838
    let mut counter: libc::c_int = 0;
839
    let mut index: libc::c_int = 0;
839
    let mut index: libc::c_int = 0;
840
    counter = 0i32;
840
    counter = 0i32;
841
    while counter < (*S_).num_bogus {
841
    while counter < (*S_).num_bogus {
842
        (*S_).bogus[counter as usize].color = rand() % 6i32 + 1i32;
842
        (*S_).bogus[counter as usize].color = rand() % 6i32 + 1i32;

876
        counter += 1
876
        counter += 1
877
    }
877
    }
878
}
878
}
879
879
880
#[no_mangle]
880
#[no_mangle]
881
pub unsafe extern "C" fn initialize_screen(S_: &mut State) {
881
unsafe extern "C" fn initialize_screen(S_: &mut State) {
882
    let mut counter: libc::c_int = 0;
882
    let mut counter: libc::c_int = 0;
883
    fmt_mvprintw(
883
    fmt_mvprintw(
884
        0i32,
884
        0i32,
885
        0i32,
885
        0i32,
886
        format_args!("robotfindskitten v{:}\n\n", unsafe {
886
        format_args!("robotfindskitten v{:}\n\n", unsafe {

903
    draw((*S_).kitten, S_);
903
    draw((*S_).kitten, S_);
904
    draw((*S_).robot, S_);
904
    draw((*S_).robot, S_);
905
    (*S_).win.as_ref().unwrap().refresh();
905
    (*S_).win.as_ref().unwrap().refresh();
906
}
906
}
907
#[no_mangle]
907
#[no_mangle]
908
pub unsafe extern "C" fn draw(mut o: screen_object, S_: &mut State) {
908
unsafe extern "C" fn draw(mut o: screen_object, S_: &mut State) {
909
    full_draw(o, 0 != 0i32, S_);
909
    full_draw(o, 0 != 0i32, S_);
910
}
910
}
911
#[no_mangle]
911
#[no_mangle]
912
pub unsafe extern "C" fn full_draw(mut o: screen_object, mut in_place: bool, S_: &mut State) {
912
unsafe extern "C" fn full_draw(mut o: screen_object, mut in_place: bool, S_: &mut State) {
913
    let mut old: attr_t = 0;
913
    let mut old: attr_t = 0;
914
    let mut dummy: libc::c_short = 0;
914
    let mut dummy: libc::c_short = 0;
915
    let mut new: attr_t = 0;
915
    let mut new: attr_t = 0;
916
    if !(stdscr as *const libc::c_void).is_null() {
916
    if !(stdscr as *const libc::c_void).is_null() {
917
        if !(&mut old as *mut attr_t as *const libc::c_void).is_null() {
917
        if !(&mut old as *mut attr_t as *const libc::c_void).is_null() {

959
        .as_ref()
959
        .as_ref()
960
        .unwrap()
960
        .unwrap()
961
        .attrset(old as libc::c_int as ::pancurses::chtype);
961
        .attrset(old as libc::c_int as ::pancurses::chtype);
962
}
962
}
963
#[no_mangle]
963
#[no_mangle]
964
pub unsafe extern "C" fn instructions(S_: &mut State) {
964
unsafe extern "C" fn instructions(S_: &mut State) {
965
    let mut dummy: libc::c_char = 0;
965
    let mut dummy: libc::c_char = 0;
966
    fmt_mvprintw(
966
    fmt_mvprintw(
967
        0i32,
967
        0i32,
968
        0i32,
968
        0i32,
969
        format_args!("robotfindskitten v{:}\n", unsafe {
969
        format_args!("robotfindskitten v{:}\n", unsafe {

1009
    (*S_).win.as_ref().unwrap().refresh();
1009
    (*S_).win.as_ref().unwrap().refresh();
1010
    dummy = ::encode_input((*S_).win.as_ref().unwrap().getch()) as libc::c_char;
1010
    dummy = ::encode_input((*S_).win.as_ref().unwrap().getch()) as libc::c_char;
1011
    (*S_).win.as_ref().unwrap().clear();
1011
    (*S_).win.as_ref().unwrap().clear();
1012
}
1012
}
1013
#[no_mangle]
1013
#[no_mangle]
1014
pub unsafe extern "C" fn draw_in_place(mut o: screen_object, S_: &mut State) {
1014
unsafe extern "C" fn draw_in_place(mut o: screen_object, S_: &mut State) {
1015
    full_draw(o, 0 != 1i32, S_);
1015
    full_draw(o, 0 != 1i32, S_);
1016
}
1016
}
1017
/*Game functions*/
1017
/*Game functions*/
1018
#[no_mangle]
1018
#[no_mangle]
1019
pub unsafe extern "C" fn play_game(S_: &mut State) {
1019
unsafe extern "C" fn play_game(S_: &mut State) {
1020
    let mut old_x: libc::c_int = (*S_).robot.x;
1020
    let mut old_x: libc::c_int = (*S_).robot.x;
1021
    let mut old_y: libc::c_int = (*S_).robot.y;
1021
    let mut old_y: libc::c_int = (*S_).robot.y;
1022
    let mut input: libc::c_int = 0;
1022
    let mut input: libc::c_int = 0;
1023
    input = ::encode_input((*S_).win.as_ref().unwrap().getch());
1023
    input = ::encode_input((*S_).win.as_ref().unwrap().getch());
1024
    while input != 27i32 && input != 'q' as i32 && input != 'Q' as i32 {
1024
    while input != 27i32 && input != 'q' as i32 && input != 'Q' as i32 {

1043
    );
1043
    );
1044
    (*S_).win.as_ref().unwrap().refresh();
1044
    (*S_).win.as_ref().unwrap().refresh();
1045
    finish(0i32);
1045
    finish(0i32);
1046
}
1046
}
1047
#[no_mangle]
1047
#[no_mangle]
1048
pub unsafe extern "C" fn message(mut message_0: *mut libc::c_char, S_: &mut State) {
1048
unsafe extern "C" fn message(mut message_0: *mut libc::c_char, S_: &mut State) {
1049
    (*S_).win.as_ref().unwrap().mv(1i32, 0i32);
1049
    (*S_).win.as_ref().unwrap().mv(1i32, 0i32);
1050
    (*S_).win.as_ref().unwrap().clrtoeol();
1050
    (*S_).win.as_ref().unwrap().clrtoeol();
1051
    fmt_mvprintw(
1051
    fmt_mvprintw(
1052
        1i32,
1052
        1i32,
1053
        0i32,
1053
        0i32,

1064
    );
1064
    );
1065
    (*S_).win.as_ref().unwrap().mv((*S_).robot.y, (*S_).robot.x);
1065
    (*S_).win.as_ref().unwrap().mv((*S_).robot.y, (*S_).robot.x);
1066
    (*S_).win.as_ref().unwrap().refresh();
1066
    (*S_).win.as_ref().unwrap().refresh();
1067
}
1067
}
1068
#[no_mangle]
1068
#[no_mangle]
1069
pub unsafe extern "C" fn process_input(mut input: libc::c_int, S_: &mut State) {
1069
unsafe extern "C" fn process_input(mut input: libc::c_int, S_: &mut State) {
1070
    let mut check_x: libc::c_int = (*S_).robot.x;
1070
    let mut check_x: libc::c_int = (*S_).robot.x;
1071
    let mut check_y: libc::c_int = (*S_).robot.y;
1071
    let mut check_y: libc::c_int = (*S_).robot.y;
1072
    match input {
1072
    match input {
1073
        12 => {
1073
        12 => {
1074
            (*S_).win.as_ref().unwrap().refresh();
1074
            (*S_).win.as_ref().unwrap().refresh();

1134
    }
1134
    }
1135
    (*S_).robot.x = check_x;
1135
    (*S_).robot.x = check_x;
1136
    (*S_).robot.y = check_y;
1136
    (*S_).robot.y = check_y;
1137
}
1137
}
1138
#[no_mangle]
1138
#[no_mangle]
1139
pub unsafe extern "C" fn play_animation(mut input: libc::c_int, S_: &mut State) {
1139
unsafe extern "C" fn play_animation(mut input: libc::c_int, S_: &mut State) {
1140
    let mut counter: libc::c_int = 0;
1140
    let mut counter: libc::c_int = 0;
1141
    counter = 4i32;
1141
    counter = 4i32;
1142
    while counter > 0i32 {
1142
    while counter > 0i32 {
1143
        if (*S_).win.as_ref().unwrap().mv(1i32, 50i32 + counter + 1i32) == -1i32 {
1143
        if (*S_).win.as_ref().unwrap().mv(1i32, 50i32 + counter + 1i32) == -1i32 {
1144
        } else {
1144
        } else {

libc calls

robotfindskitten makes a number of calls to libc functions, such as sleep and rand, using the FFI. Rust's standard library provides most of the same functionality, so we can replace these libc calls with safe equivalents.

We replace sleep with std::thread::sleep:

rewrite_expr 'sleep(__e)'
    '::std::thread::sleep(
        ::std::time::Duration::from_secs(__e as u64))' ;

Diff #50

src/robotfindskitten.rs
1159
            draw_in_place((*S_).robot, S_);
1159
            draw_in_place((*S_).robot, S_);
1160
        } else {
1160
        } else {
1161
            draw_in_place((*S_).kitten, S_);
1161
            draw_in_place((*S_).kitten, S_);
1162
        }
1162
        }
1163
        (*S_).win.as_ref().unwrap().refresh();
1163
        (*S_).win.as_ref().unwrap().refresh();
1164
        sleep(1i32 as libc::c_uint);
1164
        ::std::thread::sleep(::std::time::Duration::from_secs(
1165
            1i32 as libc::c_uint as u64,
1166
        ));
1165
        counter -= 1
1167
        counter -= 1
1166
    }
1168
    }
1167
    (*S_).win.as_ref().unwrap().mv(1i32, 0i32);
1169
    (*S_).win.as_ref().unwrap().mv(1i32, 0i32);
1168
    (*S_).win.as_ref().unwrap().addnstr(
1170
    (*S_).win.as_ref().unwrap().addnstr(
1169
        ::std::str::from_utf8(b"You found kitten! Way to go, robot!\x00")
1171
        ::std::str::from_utf8(b"You found kitten! Way to go, robot!\x00")

We replace atoi with a call to from_str:

rewrite_expr 'atoi(__e)'
    '<libc::c_int as ::std::str::FromStr>::from_str(
        ::std::ffi::CStr::from_ptr(__e).to_str().unwrap()).unwrap()' ;

Diff #51

src/robotfindskitten.rs
1206
        num_bogus: 0,
1206
        num_bogus: 0,
1207
    };
1207
    };
1208
    if argc == 1i32 {
1208
    if argc == 1i32 {
1209
        S.num_bogus = 20i32
1209
        S.num_bogus = 20i32
1210
    } else {
1210
    } else {
1211
        S.num_bogus = atoi(*argv.offset(1isize));
1211
        S.num_bogus = as ::std::str::FromStr>::from_str(
1212
            ::std::ffi::CStr::from_ptr(*argv.offset(1isize))
1213
                .to_str()
1214
                .unwrap(),
1215
        )
1216
        .unwrap();
1212
        if S.num_bogus < 0i32
1217
        if S.num_bogus < 0i32
1213
            || S.num_bogus as libc::c_ulong
1218
            || S.num_bogus as libc::c_ulong
1214
                > (::std::mem::size_of::<[*mut libc::c_char; 406]>() as libc::c_ulong)
1219
                > (::std::mem::size_of::<[*mut libc::c_char; 406]>() as libc::c_ulong)
1215
                    .wrapping_div(::std::mem::size_of::<*mut libc::c_char>() as libc::c_ulong)
1220
                    .wrapping_div(::std::mem::size_of::<*mut libc::c_char>() as libc::c_ulong)
1216
        {
1221
        {

In the version of glibc we used for translating robotfindskitten, atoi is actually provided as an inline wrapper function in the libc headers. That means the Rust translation of robotfindskitten actually includes a full definition of fn atoi(...) { ... }. Now that we've replaced the atoi call, we can delete the definition as well:

select target 'item(atoi);' ;
delete_items ;
clear_marks ;

Diff #52

src/robotfindskitten.rs
211
    pub color: libc::c_int,
211
    pub color: libc::c_int,
212
    pub bold: bool,
212
    pub bold: bool,
213
    pub character: libc::c_char,
213
    pub character: libc::c_char,
214
}
214
}
215
static ver: &'static str = "1.7320508.406\u{0}";
215
static ver: &'static str = "1.7320508.406\u{0}";
216
#[inline]
216
217
unsafe extern "C" fn atoi(mut __nptr: *const libc::c_char) -> libc::c_int {
218
    return strtol(
219
        __nptr,
220
        0 as *mut libc::c_void as *mut *mut libc::c_char,
221
        10i32,
222
    ) as libc::c_int;
223
}
224
/*Be sure to change MESSAGES when you change the array, or bad things
217
/*Be sure to change MESSAGES when you change the array, or bad things
225
will happen.*/
218
will happen.*/
226
/*Also, take note that robotfindskitten.c and configure.in
219
/*Also, take note that robotfindskitten.c and configure.in
227
currently have the version number hardcoded into them, and they
220
currently have the version number hardcoded into them, and they
228
should reflect MESSAGES. */
221
should reflect MESSAGES. */

We replace exit with std::process::exit:

rewrite_expr 'exit(__e)' '::std::process::exit(__e as i32)' ;

Diff #53

src/robotfindskitten.rs
714
    ::pancurses::endwin();
714
    ::pancurses::endwin();
715
    fmt_printf(format_args!(
715
    fmt_printf(format_args!(
716
        "{:}{:}{:}",
716
        "{:}{:}{:}",
717
        27i32 as u8 as char, '(' as i32 as u8 as char, 'B' as i32 as u8 as char
717
        27i32 as u8 as char, '(' as i32 as u8 as char, 'B' as i32 as u8 as char
718
    ));
718
    ));
719
    exit(0i32);
719
    ::std::process::exit(0i32 as i32);
720
}
720
}
721
#[no_mangle]
721
#[no_mangle]
722
unsafe extern "C" fn initialize_arrays(S_: &mut State) {
722
unsafe extern "C" fn initialize_arrays(S_: &mut State) {
723
    let mut counter: libc::c_int = 0;
723
    let mut counter: libc::c_int = 0;
724
    let mut counter2: libc::c_int = 0;
724
    let mut counter2: libc::c_int = 0;

1216
                "Run-time parameter must be between 0 and {:}.\n",
1216
                "Run-time parameter must be between 0 and {:}.\n",
1217
                (::std::mem::size_of::<[*mut libc::c_char; 406]>() as libc::c_ulong)
1217
                (::std::mem::size_of::<[*mut libc::c_char; 406]>() as libc::c_ulong)
1218
                    .wrapping_div(::std::mem::size_of::<*mut libc::c_char>() as libc::c_ulong)
1218
                    .wrapping_div(::std::mem::size_of::<*mut libc::c_char>() as libc::c_ulong)
1219
                    as libc::c_int
1219
                    as libc::c_int
1220
            ));
1220
            ));
1221
            exit(0i32);
1221
            ::std::process::exit(0i32 as i32);
1222
        }
1222
        }
1223
    }
1223
    }
1224
    srand(time(0 as *mut time_t) as libc::c_uint);
1224
    srand(time(0 as *mut time_t) as libc::c_uint);
1225
    fmt_printf(format_args!(
1225
    fmt_printf(format_args!(
1226
        "{:}{:}{:}",
1226
        "{:}{:}{:}",

For rand, no equivalent is available in the Rust standard library. Instead, we import the rand crate from crates.io:

select target 'crate;' ;
create_item 'extern crate rand;' inside ;
clear_marks ;

Diff #54

src/robotfindskitten.rs
5
    non_snake_case,
5
    non_snake_case,
6
    non_upper_case_globals,
6
    non_upper_case_globals,
7
    unused_mut
7
    unused_mut
8
)]
8
)]
9
#![feature(const_raw_ptr_to_usize_cast, extern_types, libc)]
9
#![feature(const_raw_ptr_to_usize_cast, extern_types, libc)]
10
extern crate rand;
10
struct State {
11
struct State {
11
    win: Option<::pancurses::Window>,
12
    win: Option<::pancurses::Window>,
12
    bogus: [screen_object; 406],
13
    bogus: [screen_object; 406],
13
    bogus_messages: [libc::c_int; 406],
14
    bogus_messages: [libc::c_int; 406],
14
    used_messages: [libc::c_int; 406],
15
    used_messages: [libc::c_int; 406],

robotfindskitten uses the common srand(time()) pattern to initialize the random number generator, suggesting it does not rely on the ability to control or reuse seeds. That means we can use the thread-local RNG provided by the rand crate, instead of explicitly constructing an RNG with a specific seed. So we replace rand with calls to rand::random:

rewrite_expr 'rand()'
    '(::rand::random::<libc::c_uint>() >> 1) as libc::c_int' ;

Diff #55

src/robotfindskitten.rs
783
of the first few rows of the screen. But that requires making an
783
of the first few rows of the screen. But that requires making an
784
offset function and using that everywhere. So not right now. */
784
offset function and using that everywhere. So not right now. */
785
785
786
#[no_mangle]
786
#[no_mangle]
787
unsafe extern "C" fn initialize_robot(S_: &mut State) {
787
unsafe extern "C" fn initialize_robot(S_: &mut State) {
788
    (*S_).robot.x = (::rand::random::() >> 1) as libc::c_int
788
    (*S_).robot.x = rand() % ((*S_).win.as_ref().unwrap().get_max_x() - 1i32) + 1i32;
789
        % ((*S_).win.as_ref().unwrap().get_max_x() - 1i32)
790
        + 1i32;
791
    (*S_).robot.y = (::rand::random::() >> 1) as libc::c_int
789
    (*S_).robot.y = rand() % ((*S_).win.as_ref().unwrap().get_max_y() - 1i32 - 3i32 + 1i32) + 3i32;
792
        % ((*S_).win.as_ref().unwrap().get_max_y() - 1i32 - 3i32 + 1i32)
793
        + 3i32;
790
    (*S_).robot.character = '#' as i32 as libc::c_char;
794
    (*S_).robot.character = '#' as i32 as libc::c_char;
791
    (*S_).robot.color = 0i32;
795
    (*S_).robot.color = 0i32;
792
    (*S_).robot.bold = 0 != 0i32;
796
    (*S_).robot.bold = 0 != 0i32;
793
    (*S_).screen[(*S_).robot.x as isize as usize][(*S_).robot.y as isize as usize] = 0i32;
797
    (*S_).screen[(*S_).robot.x as isize as usize][(*S_).robot.y as isize as usize] = 0i32;
794
}
798
}
795
/*Global variables. Bite me, it's fun.*/
799
/*Global variables. Bite me, it's fun.*/
796
800
797
#[no_mangle]
801
#[no_mangle]
798
unsafe extern "C" fn initialize_kitten(S_: &mut State) {
802
unsafe extern "C" fn initialize_kitten(S_: &mut State) {
799
    loop {
803
    loop {
804
        (*S_).kitten.x = (::rand::random::() >> 1) as libc::c_int
800
        (*S_).kitten.x = rand() % ((*S_).win.as_ref().unwrap().get_max_x() - 1i32) + 1i32;
805
            % ((*S_).win.as_ref().unwrap().get_max_x() - 1i32)
801
        (*S_).kitten.y =
806
            + 1i32;
807
        (*S_).kitten.y = (::rand::random::() >> 1) as libc::c_int
802
            rand() % ((*S_).win.as_ref().unwrap().get_max_y() - 1i32 - 3i32 + 1i32) + 3i32;
808
            % ((*S_).win.as_ref().unwrap().get_max_y() - 1i32 - 3i32 + 1i32)
809
            + 3i32;
803
        if !((*S_).screen[(*S_).kitten.x as isize as usize][(*S_).kitten.y as isize as usize]
810
        if !((*S_).screen[(*S_).kitten.x as isize as usize][(*S_).kitten.y as isize as usize]
804
            != -1i32)
811
            != -1i32)
805
        {
812
        {
806
            break;
813
            break;
807
        }
814
        }
808
    }
815
    }
809
    loop {
816
    loop {
810
        (*S_).kitten.character =
817
        (*S_).kitten.character = ((::rand::random::() >> 1) as libc::c_int
811
            (rand() % (126i32 - '!' as i32 + 1i32) + '!' as i32) as libc::c_char;
818
            % (126i32 - '!' as i32 + 1i32)
819
            + '!' as i32) as libc::c_char;
812
        if !(0 == validchar((*S_).kitten.character)) {
820
        if !(0 == validchar((*S_).kitten.character)) {
813
            break;
821
            break;
814
        }
822
        }
815
    }
823
    }
816
    (*S_).screen[(*S_).kitten.x as isize as usize][(*S_).kitten.y as isize as usize] = 1i32;
824
    (*S_).screen[(*S_).kitten.x as isize as usize][(*S_).kitten.y as isize as usize] = 1i32;
817
    (*S_).kitten.color = rand() % 6i32 + 1i32;
825
    (*S_).kitten.color = (::rand::random::() >> 1) as libc::c_int % 6i32 + 1i32;
818
    (*S_).kitten.bold = 0 != if 0 != rand() % 2i32 { 1i32 } else { 0i32 };
826
    (*S_).kitten.bold = 0
827
        != if 0 != (::rand::random::() >> 1) as libc::c_int % 2i32 {
828
            1i32
829
        } else {
830
            0i32
831
        };
819
}
832
}
820
833
821
/*Helper functions*/
834
/*Helper functions*/
822
#[no_mangle]
835
#[no_mangle]
823
unsafe extern "C" fn validchar(mut a: libc::c_char) -> libc::c_int {
836
unsafe extern "C" fn validchar(mut a: libc::c_char) -> libc::c_int {

831
unsafe extern "C" fn initialize_bogus(S_: &mut State) {
844
unsafe extern "C" fn initialize_bogus(S_: &mut State) {
832
    let mut counter: libc::c_int = 0;
845
    let mut counter: libc::c_int = 0;
833
    let mut index: libc::c_int = 0;
846
    let mut index: libc::c_int = 0;
834
    counter = 0i32;
847
    counter = 0i32;
835
    while counter < (*S_).num_bogus {
848
    while counter < (*S_).num_bogus {
836
        (*S_).bogus[counter as usize].color = rand() % 6i32 + 1i32;
849
        (*S_).bogus[counter as usize].color =
837
        (*S_).bogus[counter as usize].bold = 0 != if 0 != rand() % 2i32 { 1i32 } else { 0i32 };
850
            (::rand::random::() >> 1) as libc::c_int % 6i32 + 1i32;
851
        (*S_).bogus[counter as usize].bold = 0
852
            != if 0 != (::rand::random::() >> 1) as libc::c_int % 2i32 {
853
                1i32
854
            } else {
855
                0i32
856
            };
838
        loop {
857
        loop {
839
            (*S_).bogus[counter as usize].character =
858
            (*S_).bogus[counter as usize].character = ((::rand::random::() >> 1)
840
                (rand() % (126i32 - '!' as i32 + 1i32) + '!' as i32) as libc::c_char;
859
                as libc::c_int
860
                % (126i32 - '!' as i32 + 1i32)
861
                + '!' as i32) as libc::c_char;
841
            if !(0 == validchar((*S_).bogus[counter as usize].character)) {
862
            if !(0 == validchar((*S_).bogus[counter as usize].character)) {
842
                break;
863
                break;
843
            }
864
            }
844
        }
865
        }
845
        loop {
866
        loop {
846
            (*S_).bogus[counter as usize].x =
867
            (*S_).bogus[counter as usize].x = (::rand::random::() >> 1)
868
                as libc::c_int
847
                rand() % ((*S_).win.as_ref().unwrap().get_max_x() - 1i32) + 1i32;
869
                % ((*S_).win.as_ref().unwrap().get_max_x() - 1i32)
848
            (*S_).bogus[counter as usize].y =
870
                + 1i32;
871
            (*S_).bogus[counter as usize].y = (::rand::random::() >> 1)
872
                as libc::c_int
849
                rand() % ((*S_).win.as_ref().unwrap().get_max_y() - 1i32 - 3i32 + 1i32) + 3i32;
873
                % ((*S_).win.as_ref().unwrap().get_max_y() - 1i32 - 3i32 + 1i32)
874
                + 3i32;
850
            if !((*S_).screen[(*S_).bogus[counter as usize].x as isize as usize]
875
            if !((*S_).screen[(*S_).bogus[counter as usize].x as isize as usize]
851
                [(*S_).bogus[counter as usize].y as isize as usize]
876
                [(*S_).bogus[counter as usize].y as isize as usize]
852
                != -1i32)
877
                != -1i32)
853
            {
878
            {
854
                break;
879
                break;
855
            }
880
            }
856
        }
881
        }
857
        (*S_).screen[(*S_).bogus[counter as usize].x as isize as usize]
882
        (*S_).screen[(*S_).bogus[counter as usize].x as isize as usize]
858
            [(*S_).bogus[counter as usize].y as isize as usize] = counter + 2i32;
883
            [(*S_).bogus[counter as usize].y as isize as usize] = counter + 2i32;
859
        loop {
884
        loop {
860
            index = (rand() as libc::c_ulong).wrapping_rem(
885
            index = ((::rand::random::() >> 1) as libc::c_int as libc::c_ulong)
886
                .wrapping_rem(
861
                (::std::mem::size_of::<[*mut libc::c_char; 406]>() as libc::c_ulong)
887
                    (::std::mem::size_of::<[*mut libc::c_char; 406]>() as libc::c_ulong)
862
                    .wrapping_div(::std::mem::size_of::<*mut libc::c_char>() as libc::c_ulong),
888
                        .wrapping_div(::std::mem::size_of::<*mut libc::c_char>() as libc::c_ulong),
863
            ) as libc::c_int;
889
                ) as libc::c_int;
864
            if !((*S_).used_messages[index as usize] != 0i32) {
890
            if !((*S_).used_messages[index as usize] != 0i32) {
865
                break;
891
                break;
866
            }
892
            }
867
        }
893
        }
868
        (*S_).bogus_messages[counter as usize] = index;
894
        (*S_).bogus_messages[counter as usize] = index;

And we delete srand calls entirely, relying on the rand crate's automatic initialization of the thread-local RNG:

rewrite_expr 'srand(__e)' '()' ;

Diff #56

src/robotfindskitten.rs
1246
                    as libc::c_int
1246
                    as libc::c_int
1247
            ));
1247
            ));
1248
            ::std::process::exit(0i32 as i32);
1248
            ::std::process::exit(0i32 as i32);
1249
        }
1249
        }
1250
    }
1250
    }
1251
    srand(time(0 as *mut time_t) as libc::c_uint);
1251
    ();
1252
    fmt_printf(format_args!(
1252
    fmt_printf(format_args!(
1253
        "{:}{:}{:}",
1253
        "{:}{:}{:}",
1254
        27i32 as u8 as char, '(' as i32 as u8 as char, 'U' as i32 as u8 as char
1254
        27i32 as u8 as char, '(' as i32 as u8 as char, 'U' as i32 as u8 as char
1255
    ));
1255
    ));
1256
    initialize_ncurses(&mut S);
1256
    initialize_ncurses(&mut S);

At this point, the only remaining FFI call is to signal. robotfindskitten sets up a SIGINT handler to ensure that ncurses (now pancurses) is shut down properly and the terminal is returned to a normal state when the user terminates the program with ^C. Unfortunately, there is no general way to make signal handling safe: to achieve memory safety, signal handling functions must obey a number of special restrictions above and beyond Rust's normal notions of safety, and these properties cannot be checked by the Rust compiler.

We therefore leave the call to signal as unsafe code. Since this will be the only unsafe operation in the program once we finish refactoring, we wrap it in its own unsafe block:

rewrite_expr 'signal(__e, __f)' 'unsafe { signal(__e, __f) }' ;

Diff #57

src/robotfindskitten.rs
633
 *Function definitions
633
 *Function definitions
634
 */
634
 */
635
/*Initialization and setup functions*/
635
/*Initialization and setup functions*/
636
#[no_mangle]
636
#[no_mangle]
637
unsafe extern "C" fn initialize_ncurses(S_: &mut State) {
637
unsafe extern "C" fn initialize_ncurses(S_: &mut State) {
638
    signal(2i32, Some(finish));
638
    unsafe { signal(2i32, Some(finish)) };
639
    (*S_).win = Some(::pancurses::initscr());
639
    (*S_).win = Some(::pancurses::initscr());
640
    (*S_).win.as_ref().unwrap().keypad(0 != 1i32);
640
    (*S_).win.as_ref().unwrap().keypad(0 != 1i32);
641
    ::pancurses::nonl();
641
    ::pancurses::nonl();
642
    0;
642
    0;
643
    ::pancurses::noecho();
643
    ::pancurses::noecho();

We've now covered all of the libc functions used by robotfindskitten, and replaced nearly all of them with safe code.

commit

Function argument types

Two functions in robotfindskitten accept raw pointers: message takes a pointer to a string to display on the screen, and main_0 takes an array of string pointers argv containing the program's command line arguments. To make these functions safe, we must replace their raw pointer arguments with safe equivalents.

message

We begin with message because it is simpler. This function takes a single argument of type *mut c_char, which we want to replace with &str:

select target
    'item(message); child(arg); child(match_ty(*mut libc::c_char));' ;
rewrite_ty 'marked!(*mut libc::c_char)' '&str' ;
delete_marks target ;

Diff #59

src/robotfindskitten.rs
1063
    );
1063
    );
1064
    (*S_).win.as_ref().unwrap().refresh();
1064
    (*S_).win.as_ref().unwrap().refresh();
1065
    finish(0i32);
1065
    finish(0i32);
1066
}
1066
}
1067
#[no_mangle]
1067
#[no_mangle]
1068
unsafe extern "C" fn message(mut message_0: *mut libc::c_char, S_: &mut State) {
1068
unsafe extern "C" fn message(mut message_0: &str, S_: &mut State) {
1069
    (*S_).win.as_ref().unwrap().mv(1i32, 0i32);
1069
    (*S_).win.as_ref().unwrap().mv(1i32, 0i32);
1070
    (*S_).win.as_ref().unwrap().clrtoeol();
1070
    (*S_).win.as_ref().unwrap().clrtoeol();
1071
    fmt_mvprintw(
1071
    fmt_mvprintw(
1072
        1i32,
1072
        1i32,
1073
        0i32,
1073
        0i32,

Of course, simply changing the type annotation is not sufficient. Like when we retyped the ver and messages constants, this change has introduced two kinds of type errors: callers of message still pass *mut c_char where &str is now expected, and the body of message still uses the message_0: &str argument in contexts that require a *mut c_char. We fix these using type_fix_rules:

type_fix_rules
    '*, *mut __t, &str =>
        ::std::ffi::CStr::from_ptr(__old).to_str().unwrap()'
    '*, &str, *const __t =>
        ::std::ffi::CString::new(__old.to_owned()).unwrap().as_ptr()'
    ;

Diff #60

src/robotfindskitten.rs
1056
            old_y = (*S_).robot.y
1056
            old_y = (*S_).robot.y
1057
        }
1057
        }
1058
        input = ::encode_input((*S_).win.as_ref().unwrap().getch())
1058
        input = ::encode_input((*S_).win.as_ref().unwrap().getch())
1059
    }
1059
    }
1060
    message(
1060
    message(
1061
        ::std::ffi::CStr::from_ptr(
1061
        b"Bye!\x00" as *const u8 as *const libc::c_char as *mut libc::c_char,
1062
            b"Bye!\x00" as *const u8 as *const libc::c_char as *mut libc::c_char,
1063
        )
1064
        .to_str()
1065
        .unwrap(),
1062
        S_,
1066
        S_,
1063
    );
1067
    );
1064
    (*S_).win.as_ref().unwrap().refresh();
1068
    (*S_).win.as_ref().unwrap().refresh();
1065
    finish(0i32);
1069
    finish(0i32);
1066
}
1070
}

1073
        0i32,
1077
        0i32,
1074
        format_args!(
1078
        format_args!(
1075
            "{:.*}",
1079
            "{:.*}",
1076
            (*S_).win.as_ref().unwrap().get_max_x() as usize,
1080
            (*S_).win.as_ref().unwrap().get_max_x() as usize,
1077
            unsafe {
1081
            unsafe {
1078
                ::std::ffi::CStr::from_ptr(message_0 as *const libc::c_char)
1082
                ::std::ffi::CStr::from_ptr(
1079
                    .to_str()
1083
                    ::std::ffi::CString::new(message_0.to_owned())
1080
                    .unwrap()
1084
                        .unwrap()
1085
                        .as_ptr() as *const libc::c_char,
1086
                )
1087
                .to_str()
1088
                .unwrap()
1081
            }
1089
            }
1082
        ),
1090
        ),
1083
        S_,
1091
        S_,
1084
    );
1092
    );
1085
    (*S_).win.as_ref().unwrap().mv((*S_).robot.y, (*S_).robot.x);
1093
    (*S_).win.as_ref().unwrap().mv((*S_).robot.y, (*S_).robot.x);

1114
        260 | 104 | 72 | 2 => check_x -= 1,
1122
        260 | 104 | 72 | 2 => check_x -= 1,
1115
        261 | 108 | 76 | 6 => check_x += 1,
1123
        261 | 108 | 76 | 6 => check_x += 1,
1116
        0 => {}
1124
        0 => {}
1117
        _ => {
1125
        _ => {
1118
            message(
1126
            message(
1127
                ::std::ffi::CStr::from_ptr(
1119
                b"Invalid input: Use direction keys or Esc.\x00" as *const u8 as *const libc::c_char
1128
                    b"Invalid input: Use direction keys or Esc.\x00" as *const u8
1120
                    as *mut libc::c_char,
1129
                        as *const libc::c_char as *mut libc::c_char,
1130
                )
1131
                .to_str()
1132
                .unwrap(),
1121
                S_,
1133
                S_,
1122
            );
1134
            );
1123
            return;
1135
            return;
1124
        }
1136
        }
1125
    }
1137
    }

1140
                (*S_).win.as_ref().unwrap().clrtoeol();
1152
                (*S_).win.as_ref().unwrap().clrtoeol();
1141
                play_animation(input, S_);
1153
                play_animation(input, S_);
1142
            }
1154
            }
1143
            _ => {
1155
            _ => {
1144
                message(
1156
                message(
1157
                    ::std::ffi::CStr::from_ptr(
1145
                    messages[(*S_).bogus_messages[((*S_).screen[check_x as isize as usize]
1158
                        messages[(*S_).bogus_messages[((*S_).screen[check_x as isize as usize]
1146
                        [check_y as isize as usize]
1159
                            [check_y as isize as usize]
1147
                        - 2i32) as usize] as usize]
1160
                            - 2i32) as usize] as usize]
1148
                        .as_ptr() as *mut i8,
1161
                            .as_ptr() as *mut i8,
1162
                    )
1163
                    .to_str()
1164
                    .unwrap(),
1149
                    S_,
1165
                    S_,
1150
                );
1166
                );
1151
            }
1167
            }
1152
        }
1168
        }
1153
        return;
1169
        return;

The first rule handles callers of message, using CStr methods to convert their *mut c_char raw pointers into safe &str references. The second handles errors in the body of message, using CString to convert &strs back into *const c_char. Note we must use CString instead of CStr in the second rule because an allocation is required: a &str is not guaranteed to end with a null terminator, so CString must copy it into a larger buffer and add the null terminator to produce a valid *const c_char string pointer. Since the CString is temporary, it will be deallocated at the end of the containing expression, but this is good enough for the code we encounter inside of message. More complex string manipulation, however, would likely require a different refactoring approach.

main_0

The Rust function main_0 is the translation of the C main function of robotfindskitten. The Rust main is a c2rust-generated wrapper that handles the differences between C's main signature and Rust's before invoking main_0.

As in the message case, we wish to replace the unsafe pointer types in main_0's argument list with safe equivalents. However, in this case our choice of safe reference type is more constrained. main_0 calls argv.offset to access the individual command-line arguments, so we must use CArray (which supports such access patterns) for the outer pointer. For the inner pointer, we use Option<&CStr>: CStr supports the conversions we will need to perform in main and main_0, and Option<&CStr> can be safely zero-initialized, which is required by CArray.

We begin, as with message, by rewriting the argument type:

select target
    'item(main_0); child(arg && name("argv")); child(ty);' ;
rewrite_ty 'marked!(*mut *mut libc::c_char)'
    '::c2rust_runtime::CArray<Option<&::std::ffi::CStr>>' ;
delete_marks target ;

Diff #61

src/robotfindskitten.rs
1210
        -1i32 as usize,
1210
        -1i32 as usize,
1211
    );
1211
    );
1212
    (*S_).win.as_ref().unwrap().refresh();
1212
    (*S_).win.as_ref().unwrap().refresh();
1213
    finish(0i32);
1213
    finish(0i32);
1214
}
1214
}
1215
unsafe fn main_0(mut argc: libc::c_int, mut argv: *mut *mut libc::c_char) -> libc::c_int {
1215
unsafe fn main_0(
1216
    mut argc: libc::c_int,
1217
    mut argv: ::c2rust_runtime::CArray<Option<&::std::ffi::CStr>>,
1218
) -> libc::c_int {
1216
    let mut S: State = State {
1219
    let mut S: State = State {
1217
        win: None,
1220
        win: None,
1218
        bogus: [screen_object {
1221
        bogus: [screen_object {
1219
            x: 0,
1222
            x: 0,
1220
            y: 0,
1223
            y: 0,

Next, we fix type errors in main, which is the only caller of main_0. Since c2rust always generates the same main wrapper function, rather than refactor it, we can simply replace it entirely with a new version that is compatible with main_0's new signature:

select target 'item(main);' ;
create_item '
    fn main() {
        // Collect argv into a vector.
        let mut args_owned: Vec<::std::ffi::CString> = Vec::new();
        for arg in ::std::env::args() {
            args_owned.push(::std::ffi::CString::new(arg).unwrap());
        }

        // Now that the length is known, we can build a CArray.
        let mut args: ::c2rust_runtime::CArray<Option<&::std::ffi::CStr>> =
            ::c2rust_runtime::CArray::alloc(args_owned.len() + 1);
        for i in 0 .. args_owned.len() {
            args[i] = Some(&args_owned[i]);
        }
        // The last element of `args` remains `None`.

        unsafe {
            ::std::process::exit(main_0(
                (args.len() - 1) as libc::c_int,
                args) as i32);
        }
    }
' after ;
delete_items ;
clear_marks ;

Diff #62

src/robotfindskitten.rs
1280
    instructions(&mut S);
1280
    instructions(&mut S);
1281
    initialize_screen(&mut S);
1281
    initialize_screen(&mut S);
1282
    play_game(&mut S);
1282
    play_game(&mut S);
1283
    return 0;
1283
    return 0;
1284
}
1284
}
1285
pub fn main() {
1285
fn main() {
1286
    let mut args: Vec<*mut libc::c_char> = Vec::new();
1286
    // Collect argv into a vector.
1287
    let mut args_owned: Vec<::std::ffi::CString> = Vec::new();
1287
    for arg in ::std::env::args() {
1288
    for arg in ::std::env::args() {
1288
        args.push(
1289
        args_owned.push(::std::ffi::CString::new(arg).unwrap());
1289
            ::std::ffi::CString::new(arg)
1290
                .expect("Failed to convert argument into CString.")
1291
                .into_raw(),
1292
        );
1293
    }
1290
    }
1294
    args.push(::std::ptr::null_mut());
1291
1292
    // Now that the length is known, we can build a CArray.
1293
    let mut args: ::c2rust_runtime::CArray<Option<&::std::ffi::CStr>> =
1294
        ::c2rust_runtime::CArray::alloc(args_owned.len() + 1);
1295
    for i in 0..args_owned.len() {
1296
        args[i] = Some(&args_owned[i]);
1297
    }
1298
    // The last element of `args` remains `None`.
1299
1295
    unsafe {
1300
    unsafe {
1296
        ::std::process::exit(main_0(
1301
        ::std::process::exit(main_0((args.len() - 1) as libc::c_int, args) as i32);
1297
            (args.len() - 1) as libc::c_int,
1298
            args.as_mut_ptr() as *mut *mut libc::c_char,
1299
        ) as i32)
1300
    }
1302
    }
1301
}
1303
}

Now to fix errors in main_0 itself. We changed both the inner and outer pointer types of argv, so there are two kinds of errors to clean up.

For the outer pointer, where we changed *mut T to CArray<T>, the problem we see is that argv.offset(...) returns &CArrayOffset<T>, not *mut T, and &CArrayOffset<T> requires two derefs to obtain a T (&CArrayOffset<T> derefs to CArrayOffset<T>, which derefs to T) instead of just one. We handle this with type_fix_rules, looking for cases where a single deref resulted in CArrayOffset<T> but some other type was expected, and adding the second deref:

type_fix_rules
    '*, ::c2rust_runtime::array::CArrayOffset<__t>, __u => *__old'
    ;

Diff #63

src/robotfindskitten.rs
1246
    };
1246
    };
1247
    if argc == 1i32 {
1247
    if argc == 1i32 {
1248
        S.num_bogus = 20i32
1248
        S.num_bogus = 20i32
1249
    } else {
1249
    } else {
1250
        S.num_bogus = as ::std::str::FromStr>::from_str(
1250
        S.num_bogus = as ::std::str::FromStr>::from_str(
1251
            ::std::ffi::CStr::from_ptr(*argv.offset(1isize))
1251
            ::std::ffi::CStr::from_ptr(**argv.offset(1isize))
1252
                .to_str()
1252
                .to_str()
1253
                .unwrap(),
1253
                .unwrap(),
1254
        )
1254
        )
1255
        .unwrap();
1255
        .unwrap();
1256
        if S.num_bogus < 0i32
1256
        if S.num_bogus < 0i32

For the inner pointer type, which we changed from *mut c_char to Option<&CStr>, we need only insert a simple conversion anywhere the new type is used but *mut c_char is expected:

type_fix_rules
    '*, ::std::option::Option<&::std::ffi::CStr>, *const i8 =>
        opt_c_str_to_ptr(__old)'
    ;

Diff #64

src/robotfindskitten.rs
1246
    };
1246
    };
1247
    if argc == 1i32 {
1247
    if argc == 1i32 {
1248
        S.num_bogus = 20i32
1248
        S.num_bogus = 20i32
1249
    } else {
1249
    } else {
1250
        S.num_bogus = as ::std::str::FromStr>::from_str(
1250
        S.num_bogus = as ::std::str::FromStr>::from_str(
1251
            ::std::ffi::CStr::from_ptr(**argv.offset(1isize))
1251
            ::std::ffi::CStr::from_ptr(opt_c_str_to_ptr(**argv.offset(1isize)))
1252
                .to_str()
1252
                .to_str()
1253
                .unwrap(),
1253
                .unwrap(),
1254
        )
1254
        )
1255
        .unwrap();
1255
        .unwrap();
1256
        if S.num_bogus < 0i32
1256
        if S.num_bogus < 0i32

The only quirk here is that we wrap up the conversion in a helper function, making it easier to recognize in the later refactoring step where we clean up redundant string conversions. Of course, now we must define that helper function:

select target 'item(main);' ;
create_item '
    fn opt_c_str_to_ptr(x: Option<&::std::ffi::CStr>) -> *const libc::c_char {
        match x {
            None => ::std::ptr::null(),
            Some(x) => x.as_ptr(),
        }
    }
' after ;
clear_marks ;

Diff #65

src/robotfindskitten.rs
1299
1299
1300
    unsafe {
1300
    unsafe {
1301
        ::std::process::exit(main_0((args.len() - 1) as libc::c_int, args) as i32);
1301
        ::std::process::exit(main_0((args.len() - 1) as libc::c_int, args) as i32);
1302
    }
1302
    }
1303
}
1303
}
1304
fn opt_c_str_to_ptr(x: Option<&::std::ffi::CStr>) -> *const libc::c_char {
1305
    match x {
1306
        None => ::std::ptr::null(),
1307
        Some(x) => x.as_ptr(),
1308
    }
1309
}

And with that, we are done. All raw pointer arguments in robotfindskitten have now been replaced with safe equivalents.

commit

String conversion cleanup

A number of the previous refactoring steps involved changing the type of some variable from a raw C string (*const c_char) to a safe Rust string (&str), inserting conversions between the two forms everywhere the variable was initialized or used. But now that we have finished transitioning the entire crate to Rust strings, many of those conversions have become redundant. Essentially, we began with code like this:

fn f(s1: *const c_char) { ... }

fn g(s2: *const c_char) {
    ... f(s2) ...
}

By incrementally refactoring C strings into Rust string, we first transitioned to code like this:

fn f(s1: &str) { ... }

fn g(s2: *const c_char) {
    ... f(c_str_to_rust(s2)) ...
}

And then to code like this:

fn f(s1: &str) { ... }

fn g(s2: &str) {
    ... f(rust_str_to_c(c_str_to_rust(s2))) ...
}

But rust_str_to_c(c_str_to_rust(s2)) is the same as just s2 - the two conversions are redundant and can be removed:

fn f(s1: &str) { ... }

fn g(s2: &str) {
    ... f(s2) ...
}

This doesn't merely affect readability - the actual conversion operations represented by c_str_to_rust are unsafe, so we must remove them to complete our refactoring of robotfindskitten.

The actual refactoring process we apply to robotfindskitten mostly consists of removing specific types of redundant conversions with rewrite_expr. The patterns we use here are general, taking advantage of overlap between different conversion cases rather than hardcoding a rewrite for each distinct conversion in robotfindskitten.

To begin with, converting CString to *const c_char to CStr can be replaced with a no-op (CString derefs to CStr, so it can be used almost anywhere a CStr is required):

rewrite_expr
    '::std::ffi::CStr::from_ptr(
        cast!(typed!(__e, ::std::ffi::CString).as_ptr()))'
    '__e' ;

Diff #67

src/robotfindskitten.rs
1077
        0i32,
1077
        0i32,
1078
        format_args!(
1078
        format_args!(
1079
            "{:.*}",
1079
            "{:.*}",
1080
            (*S_).win.as_ref().unwrap().get_max_x() as usize,
1080
            (*S_).win.as_ref().unwrap().get_max_x() as usize,
1081
            unsafe {
1081
            unsafe {
1082
                ::std::ffi::CStr::from_ptr(
1083
                    ::std::ffi::CString::new(message_0.to_owned())
1082
                ::std::ffi::CString::new(message_0.to_owned())
1084
                        .unwrap()
1083
                    .unwrap()
1085
                        .as_ptr() as *const libc::c_char,
1086
                )
1087
                .to_str()
1084
                    .to_str()
1088
                .unwrap()
1085
                    .unwrap()
1089
            }
1086
            }
1090
        ),
1087
        ),
1091
        S_,
1088
        S_,
1092
    );
1089
    );
1093
    (*S_).win.as_ref().unwrap().mv((*S_).robot.y, (*S_).robot.x);
1090
    (*S_).win.as_ref().unwrap().mv((*S_).robot.y, (*S_).robot.x);

Converting String to CString to Option<&str> is not strictly a no-op, but can still be simplified:

rewrite_expr
    '::std::ffi::CString::new(__e).unwrap().to_str()'
    'Some(&__e)' ;

Diff #68

src/robotfindskitten.rs
1076
        1i32,
1076
        1i32,
1077
        0i32,
1077
        0i32,
1078
        format_args!(
1078
        format_args!(
1079
            "{:.*}",
1079
            "{:.*}",
1080
            (*S_).win.as_ref().unwrap().get_max_x() as usize,
1080
            (*S_).win.as_ref().unwrap().get_max_x() as usize,
1081
            unsafe {
1081
            unsafe { Some(&message_0.to_owned()).unwrap() }
1082
                ::std::ffi::CString::new(message_0.to_owned())
1083
                    .unwrap()
1084
                    .to_str()
1085
                    .unwrap()
1086
            }
1087
        ),
1082
        ),
1088
        S_,
1083
        S_,
1089
    );
1084
    );
1090
    (*S_).win.as_ref().unwrap().mv((*S_).robot.y, (*S_).robot.x);
1085
    (*S_).win.as_ref().unwrap().mv((*S_).robot.y, (*S_).robot.x);
1091
    (*S_).win.as_ref().unwrap().refresh();
1086
    (*S_).win.as_ref().unwrap().refresh();

In some places, the code actually converts &str to *const c_char directly, rather than using CString, and then converts *const c_char to CStr to &str. This is memory-safe only when the &str already includes a null terminator, and the CStr to str conversion will trim it off. We rewrite the code to simply trim off the null terminator directly, avoiding these complex (and unsafe) conversions:

rewrite_expr
    '::std::ffi::CStr::from_ptr(
        cast!(typed!(__e, &str).as_ptr())).to_str()'
    "Some(__e.trim_end_matches('\0'))" ;

Diff #69

src/robotfindskitten.rs
902
    let mut counter: libc::c_int = 0;
902
    let mut counter: libc::c_int = 0;
903
    fmt_mvprintw(
903
    fmt_mvprintw(
904
        0i32,
904
        0i32,
905
        0i32,
905
        0i32,
906
        format_args!("robotfindskitten v{:}\n\n", unsafe {
906
        format_args!("robotfindskitten v{:}\n\n", unsafe {
907
            ::std::ffi::CStr::from_ptr(ver.as_ptr() as *const libc::c_char)
907
            Some(ver.trim_end_matches('\u{0}')).unwrap()
908
                .to_str()
909
                .unwrap()
910
        }),
908
        }),
911
        S_,
909
        S_,
912
    );
910
    );
913
    counter = 0i32;
911
    counter = 0i32;
914
    while counter <= (*S_).win.as_ref().unwrap().get_max_x() - 1i32 {
912
    while counter <= (*S_).win.as_ref().unwrap().get_max_x() - 1i32 {

985
    let mut dummy: libc::c_char = 0;
983
    let mut dummy: libc::c_char = 0;
986
    fmt_mvprintw(
984
    fmt_mvprintw(
987
        0i32,
985
        0i32,
988
        0i32,
986
        0i32,
989
        format_args!("robotfindskitten v{:}\n", unsafe {
987
        format_args!("robotfindskitten v{:}\n", unsafe {
990
            ::std::ffi::CStr::from_ptr(ver.as_ptr() as *const libc::c_char)
988
            Some(ver.trim_end_matches('\u{0}')).unwrap()
991
                .to_str()
992
                .unwrap()
993
        }),
989
        }),
994
        S_,
990
        S_,
995
    );
991
    );
996
    fmt_printw(
992
    fmt_printw(
997
        format_args!("By the illustrious Leonard Richardson (C) 1997, 2000\n"),
993
        format_args!("By the illustrious Leonard Richardson (C) 1997, 2000\n"),

1144
                (*S_).win.as_ref().unwrap().clrtoeol();
1140
                (*S_).win.as_ref().unwrap().clrtoeol();
1145
                play_animation(input, S_);
1141
                play_animation(input, S_);
1146
            }
1142
            }
1147
            _ => {
1143
            _ => {
1148
                message(
1144
                message(
1149
                    ::std::ffi::CStr::from_ptr(
1145
                    Some(
1150
                        messages[(*S_).bogus_messages[((*S_).screen[check_x as isize as usize]
1146
                        messages[(*S_).bogus_messages[((*S_).screen[check_x as isize as usize]
1151
                            [check_y as isize as usize]
1147
                            [check_y as isize as usize]
1152
                            - 2i32) as usize] as usize]
1148
                            - 2i32) as usize] as usize]
1153
                            .as_ptr() as *mut i8,
1149
                            .trim_end_matches('\u{0}'),
1154
                    )
1150
                    )
1155
                    .to_str()
1156
                    .unwrap(),
1151
                    .unwrap(),
1157
                    S_,
1152
                    S_,
1158
                );
1153
                );
1159
            }
1154
            }
1160
        }
1155
        }

For code in main_0 using the opt_c_str_to_ptr helper function we introduced earlier, the Option<&CStr> to &CStr conversion can be replaced with a simple unwrap():

rewrite_expr
    '::std::ffi::CStr::from_ptr(cast!(opt_c_str_to_ptr(__e)))'
    '__e.unwrap()' ;

Diff #70

src/robotfindskitten.rs
1233
    };
1233
    };
1234
    if argc == 1i32 {
1234
    if argc == 1i32 {
1235
        S.num_bogus = 20i32
1235
        S.num_bogus = 20i32
1236
    } else {
1236
    } else {
1237
        S.num_bogus = as ::std::str::FromStr>::from_str(
1237
        S.num_bogus = as ::std::str::FromStr>::from_str(
1238
            ::std::ffi::CStr::from_ptr(opt_c_str_to_ptr(**argv.offset(1isize)))
1238
            (**argv.offset(1isize)).unwrap().to_str().unwrap(),
1239
                .to_str()
1240
                .unwrap(),
1241
        )
1239
        )
1242
        .unwrap();
1240
        .unwrap();
1243
        if S.num_bogus < 0i32
1241
        if S.num_bogus < 0i32
1244
            || S.num_bogus as libc::c_ulong
1242
            || S.num_bogus as libc::c_ulong
1245
                > (::std::mem::size_of::<[*mut libc::c_char; 406]>() as libc::c_ulong)
1243
                > (::std::mem::size_of::<[*mut libc::c_char; 406]>() as libc::c_ulong)

Conversions of bytestring literals (b"...", whose type is &[u8; _]) to *const c_char to CStr to str can be simplified down to a direct conversion from &[u8; _] to &str, plus removal of the null terminator:

rewrite_expr
    '::std::ffi::CStr::from_ptr(
        cast!(typed!(__e, &[u8; __f]))).to_str()'
    "Some(::std::str::from_utf8(__e).unwrap().trim_end_matches('\0'))" ;

Diff #71

src/robotfindskitten.rs
1052
            old_y = (*S_).robot.y
1052
            old_y = (*S_).robot.y
1053
        }
1053
        }
1054
        input = ::encode_input((*S_).win.as_ref().unwrap().getch())
1054
        input = ::encode_input((*S_).win.as_ref().unwrap().getch())
1055
    }
1055
    }
1056
    message(
1056
    message(
1057
        ::std::ffi::CStr::from_ptr(
1057
        Some(
1058
            b"Bye!\x00" as *const u8 as *const libc::c_char as *mut libc::c_char,
1058
            ::std::str::from_utf8(b"Bye!\x00")
1059
                .unwrap()
1060
                .trim_end_matches('\u{0}'),
1059
        )
1061
        )
1060
        .to_str()
1061
        .unwrap(),
1062
        .unwrap(),
1062
        S_,
1063
        S_,
1063
    );
1064
    );
1064
    (*S_).win.as_ref().unwrap().refresh();
1065
    (*S_).win.as_ref().unwrap().refresh();
1065
    finish(0i32);
1066
    finish(0i32);

1110
        260 | 104 | 72 | 2 => check_x -= 1,
1111
        260 | 104 | 72 | 2 => check_x -= 1,
1111
        261 | 108 | 76 | 6 => check_x += 1,
1112
        261 | 108 | 76 | 6 => check_x += 1,
1112
        0 => {}
1113
        0 => {}
1113
        _ => {
1114
        _ => {
1114
            message(
1115
            message(
1115
                ::std::ffi::CStr::from_ptr(
1116
                Some(
1116
                    b"Invalid input: Use direction keys or Esc.\x00" as *const u8
1117
                    ::std::str::from_utf8(b"Invalid input: Use direction keys or Esc.\x00")
1117
                        as *const libc::c_char as *mut libc::c_char,
1118
                        .unwrap()
1119
                        .trim_end_matches('\u{0}'),
1118
                )
1120
                )
1119
                .to_str()
1120
                .unwrap(),
1121
                .unwrap(),
1121
                S_,
1122
                S_,
1122
            );
1123
            );
1123
            return;
1124
            return;
1124
        }
1125
        }

This removes the unsafety, but with a little more work, we can further improve readability. First, we convert the byte strings to ordinary string literals (b"..." to "..."):

select target
    'crate; desc(match_expr(::std::str::from_utf8(__e))); desc(expr);' ;
bytestr_to_str ;
clear_marks ;

Diff #72

src/robotfindskitten.rs
1053
        }
1053
        }
1054
        input = ::encode_input((*S_).win.as_ref().unwrap().getch())
1054
        input = ::encode_input((*S_).win.as_ref().unwrap().getch())
1055
    }
1055
    }
1056
    message(
1056
    message(
1057
        Some(
1057
        Some(
1058
            ::std::str::from_utf8(b"Bye!\x00")
1058
            ::std::str::from_utf8("Bye!\u{0}")
1059
                .unwrap()
1059
                .unwrap()
1060
                .trim_end_matches('\u{0}'),
1060
                .trim_end_matches('\u{0}'),
1061
        )
1061
        )
1062
        .unwrap(),
1062
        .unwrap(),
1063
        S_,
1063
        S_,

1112
        261 | 108 | 76 | 6 => check_x += 1,
1112
        261 | 108 | 76 | 6 => check_x += 1,
1113
        0 => {}
1113
        0 => {}
1114
        _ => {
1114
        _ => {
1115
            message(
1115
            message(
1116
                Some(
1116
                Some(
1117
                    ::std::str::from_utf8(b"Invalid input: Use direction keys or Esc.\x00")
1117
                    ::std::str::from_utf8("Invalid input: Use direction keys or Esc.\u{0}")
1118
                        .unwrap()
1118
                        .unwrap()
1119
                        .trim_end_matches('\u{0}'),
1119
                        .trim_end_matches('\u{0}'),
1120
                )
1120
                )
1121
                .unwrap(),
1121
                .unwrap(),
1122
                S_,
1122
                S_,

1190
        ));
1190
        ));
1191
        counter -= 1
1191
        counter -= 1
1192
    }
1192
    }
1193
    (*S_).win.as_ref().unwrap().mv(1i32, 0i32);
1193
    (*S_).win.as_ref().unwrap().mv(1i32, 0i32);
1194
    (*S_).win.as_ref().unwrap().addnstr(
1194
    (*S_).win.as_ref().unwrap().addnstr(
1195
        ::std::str::from_utf8(b"You found kitten! Way to go, robot!\x00")
1195
        ::std::str::from_utf8("You found kitten! Way to go, robot!\u{0}")
1196
            .unwrap()
1196
            .unwrap()
1197
            .trim_end_matches('\u{0}'),
1197
            .trim_end_matches('\u{0}'),
1198
        -1i32 as usize,
1198
        -1i32 as usize,
1199
    );
1199
    );
1200
    (*S_).win.as_ref().unwrap().refresh();
1200
    (*S_).win.as_ref().unwrap().refresh();

This introduces type errors, as the type of the literal has changed from &str to &[u8]. We fix these by inserting calls to str::as_bytes:

type_fix_rules '*, &str, &[u8] => __old.as_bytes()' ;

Diff #73

src/robotfindskitten.rs
1053
        }
1053
        }
1054
        input = ::encode_input((*S_).win.as_ref().unwrap().getch())
1054
        input = ::encode_input((*S_).win.as_ref().unwrap().getch())
1055
    }
1055
    }
1056
    message(
1056
    message(
1057
        Some(
1057
        Some(
1058
            ::std::str::from_utf8("Bye!\u{0}")
1058
            ::std::str::from_utf8("Bye!\u{0}".as_bytes())
1059
                .unwrap()
1059
                .unwrap()
1060
                .trim_end_matches('\u{0}'),
1060
                .trim_end_matches('\u{0}'),
1061
        )
1061
        )
1062
        .unwrap(),
1062
        .unwrap(),
1063
        S_,
1063
        S_,

1112
        261 | 108 | 76 | 6 => check_x += 1,
1112
        261 | 108 | 76 | 6 => check_x += 1,
1113
        0 => {}
1113
        0 => {}
1114
        _ => {
1114
        _ => {
1115
            message(
1115
            message(
1116
                Some(
1116
                Some(
1117
                    ::std::str::from_utf8(
1117
                    ::std::str::from_utf8("Invalid input: Use direction keys or Esc.\u{0}")
1118
                        "Invalid input: Use direction keys or Esc.\u{0}".as_bytes(),
1119
                    )
1118
                        .unwrap()
1120
                    .unwrap()
1119
                        .trim_end_matches('\u{0}'),
1121
                    .trim_end_matches('\u{0}'),
1120
                )
1122
                )
1121
                .unwrap(),
1123
                .unwrap(),
1122
                S_,
1124
                S_,
1123
            );
1125
            );
1124
            return;
1126
            return;

1190
        ));
1192
        ));
1191
        counter -= 1
1193
        counter -= 1
1192
    }
1194
    }
1193
    (*S_).win.as_ref().unwrap().mv(1i32, 0i32);
1195
    (*S_).win.as_ref().unwrap().mv(1i32, 0i32);
1194
    (*S_).win.as_ref().unwrap().addnstr(
1196
    (*S_).win.as_ref().unwrap().addnstr(
1195
        ::std::str::from_utf8("You found kitten! Way to go, robot!\u{0}")
1197
        ::std::str::from_utf8("You found kitten! Way to go, robot!\u{0}".as_bytes())
1196
            .unwrap()
1198
            .unwrap()
1197
            .trim_end_matches('\u{0}'),
1199
            .trim_end_matches('\u{0}'),
1198
        -1i32 as usize,
1200
        -1i32 as usize,
1199
    );
1201
    );
1200
    (*S_).win.as_ref().unwrap().refresh();
1202
    (*S_).win.as_ref().unwrap().refresh();

Finally, we remove the redundant conversion from &str to &[u8] to &str:

rewrite_expr
    '::std::str::from_utf8(__e.as_bytes())'
    'Some(__e)' ;

Diff #74

src/robotfindskitten.rs
1052
            old_y = (*S_).robot.y
1052
            old_y = (*S_).robot.y
1053
        }
1053
        }
1054
        input = ::encode_input((*S_).win.as_ref().unwrap().getch())
1054
        input = ::encode_input((*S_).win.as_ref().unwrap().getch())
1055
    }
1055
    }
1056
    message(
1056
    message(
1057
        Some(
1057
        Some(Some("Bye!\u{0}").unwrap().trim_end_matches('\u{0}')).unwrap(),
1058
            ::std::str::from_utf8("Bye!\u{0}".as_bytes())
1059
                .unwrap()
1060
                .trim_end_matches('\u{0}'),
1061
        )
1062
        .unwrap(),
1063
        S_,
1058
        S_,
1064
    );
1059
    );
1065
    (*S_).win.as_ref().unwrap().refresh();
1060
    (*S_).win.as_ref().unwrap().refresh();
1066
    finish(0i32);
1061
    finish(0i32);
1067
}
1062
}

1112
        261 | 108 | 76 | 6 => check_x += 1,
1107
        261 | 108 | 76 | 6 => check_x += 1,
1113
        0 => {}
1108
        0 => {}
1114
        _ => {
1109
        _ => {
1115
            message(
1110
            message(
1116
                Some(
1111
                Some(
1117
                    ::std::str::from_utf8(
1118
                        "Invalid input: Use direction keys or Esc.\u{0}".as_bytes(),
1112
                    Some("Invalid input: Use direction keys or Esc.\u{0}")
1119
                    )
1120
                    .unwrap()
1113
                        .unwrap()
1121
                    .trim_end_matches('\u{0}'),
1114
                        .trim_end_matches('\u{0}'),
1122
                )
1115
                )
1123
                .unwrap(),
1116
                .unwrap(),
1124
                S_,
1117
                S_,
1125
            );
1118
            );
1126
            return;
1119
            return;

1192
        ));
1185
        ));
1193
        counter -= 1
1186
        counter -= 1
1194
    }
1187
    }
1195
    (*S_).win.as_ref().unwrap().mv(1i32, 0i32);
1188
    (*S_).win.as_ref().unwrap().mv(1i32, 0i32);
1196
    (*S_).win.as_ref().unwrap().addnstr(
1189
    (*S_).win.as_ref().unwrap().addnstr(
1197
        ::std::str::from_utf8("You found kitten! Way to go, robot!\u{0}".as_bytes())
1190
        Some("You found kitten! Way to go, robot!\u{0}")
1198
            .unwrap()
1191
            .unwrap()
1199
            .trim_end_matches('\u{0}'),
1192
            .trim_end_matches('\u{0}'),
1200
        -1i32 as usize,
1193
        -1i32 as usize,
1201
    );
1194
    );
1202
    (*S_).win.as_ref().unwrap().refresh();
1195
    (*S_).win.as_ref().unwrap().refresh();

With the replacements above, we have removed all redundant string conversions from the crate. This was the last major source of unnecessary unsafety in robotfindskitten.

The last few changes we make are purely cosmetic - they do not affect safety. First, Some(x).unwrap() is the same as just x:

rewrite_expr
    'Some(__x).unwrap()'
    '__x' ;

Diff #75

src/robotfindskitten.rs
902
    let mut counter: libc::c_int = 0;
902
    let mut counter: libc::c_int = 0;
903
    fmt_mvprintw(
903
    fmt_mvprintw(
904
        0i32,
904
        0i32,
905
        0i32,
905
        0i32,
906
        format_args!("robotfindskitten v{:}\n\n", unsafe {
906
        format_args!("robotfindskitten v{:}\n\n", unsafe {
907
            Some(ver.trim_end_matches('\u{0}')).unwrap()
907
            ver.trim_end_matches('\u{0}')
908
        }),
908
        }),
909
        S_,
909
        S_,
910
    );
910
    );
911
    counter = 0i32;
911
    counter = 0i32;
912
    while counter <= (*S_).win.as_ref().unwrap().get_max_x() - 1i32 {
912
    while counter <= (*S_).win.as_ref().unwrap().get_max_x() - 1i32 {

983
    let mut dummy: libc::c_char = 0;
983
    let mut dummy: libc::c_char = 0;
984
    fmt_mvprintw(
984
    fmt_mvprintw(
985
        0i32,
985
        0i32,
986
        0i32,
986
        0i32,
987
        format_args!("robotfindskitten v{:}\n", unsafe {
987
        format_args!("robotfindskitten v{:}\n", unsafe {
988
            Some(ver.trim_end_matches('\u{0}')).unwrap()
988
            ver.trim_end_matches('\u{0}')
989
        }),
989
        }),
990
        S_,
990
        S_,
991
    );
991
    );
992
    fmt_printw(
992
    fmt_printw(
993
        format_args!("By the illustrious Leonard Richardson (C) 1997, 2000\n"),
993
        format_args!("By the illustrious Leonard Richardson (C) 1997, 2000\n"),

1051
            old_x = (*S_).robot.x;
1051
            old_x = (*S_).robot.x;
1052
            old_y = (*S_).robot.y
1052
            old_y = (*S_).robot.y
1053
        }
1053
        }
1054
        input = ::encode_input((*S_).win.as_ref().unwrap().getch())
1054
        input = ::encode_input((*S_).win.as_ref().unwrap().getch())
1055
    }
1055
    }
1056
    message(
1056
    message("Bye!\u{0}".trim_end_matches('\u{0}'), S_);
1057
        Some(Some("Bye!\u{0}").unwrap().trim_end_matches('\u{0}')).unwrap(),
1058
        S_,
1059
    );
1060
    (*S_).win.as_ref().unwrap().refresh();
1057
    (*S_).win.as_ref().unwrap().refresh();
1061
    finish(0i32);
1058
    finish(0i32);
1062
}
1059
}
1063
#[no_mangle]
1060
#[no_mangle]
1064
unsafe extern "C" fn message(mut message_0: &str, S_: &mut State) {
1061
unsafe extern "C" fn message(mut message_0: &str, S_: &mut State) {

1068
        1i32,
1065
        1i32,
1069
        0i32,
1066
        0i32,
1070
        format_args!(
1067
        format_args!(
1071
            "{:.*}",
1068
            "{:.*}",
1072
            (*S_).win.as_ref().unwrap().get_max_x() as usize,
1069
            (*S_).win.as_ref().unwrap().get_max_x() as usize,
1073
            unsafe { Some(&message_0.to_owned()).unwrap() }
1070
            unsafe { &message_0.to_owned() }
1074
        ),
1071
        ),
1075
        S_,
1072
        S_,
1076
    );
1073
    );
1077
    (*S_).win.as_ref().unwrap().mv((*S_).robot.y, (*S_).robot.x);
1074
    (*S_).win.as_ref().unwrap().mv((*S_).robot.y, (*S_).robot.x);
1078
    (*S_).win.as_ref().unwrap().refresh();
1075
    (*S_).win.as_ref().unwrap().refresh();

1106
        260 | 104 | 72 | 2 => check_x -= 1,
1103
        260 | 104 | 72 | 2 => check_x -= 1,
1107
        261 | 108 | 76 | 6 => check_x += 1,
1104
        261 | 108 | 76 | 6 => check_x += 1,
1108
        0 => {}
1105
        0 => {}
1109
        _ => {
1106
        _ => {
1110
            message(
1107
            message(
1111
                Some(
1112
                    Some("Invalid input: Use direction keys or Esc.\u{0}")
1108
                "Invalid input: Use direction keys or Esc.\u{0}".trim_end_matches('\u{0}'),
1113
                        .unwrap()
1114
                        .trim_end_matches('\u{0}'),
1115
                )
1116
                .unwrap(),
1117
                S_,
1109
                S_,
1118
            );
1110
            );
1119
            return;
1111
            return;
1120
        }
1112
        }
1121
    }
1113
    }

1136
                (*S_).win.as_ref().unwrap().clrtoeol();
1128
                (*S_).win.as_ref().unwrap().clrtoeol();
1137
                play_animation(input, S_);
1129
                play_animation(input, S_);
1138
            }
1130
            }
1139
            _ => {
1131
            _ => {
1140
                message(
1132
                message(
1141
                    Some(
1142
                        messages[(*S_).bogus_messages[((*S_).screen[check_x as isize as usize]
1133
                    messages[(*S_).bogus_messages[((*S_).screen[check_x as isize as usize]
1143
                            [check_y as isize as usize]
1134
                        [check_y as isize as usize]
1144
                            - 2i32) as usize] as usize]
1135
                        - 2i32) as usize] as usize]
1145
                            .trim_end_matches('\u{0}'),
1136
                        .trim_end_matches('\u{0}'),
1146
                    )
1147
                    .unwrap(),
1148
                    S_,
1137
                    S_,
1149
                );
1138
                );
1150
            }
1139
            }
1151
        }
1140
        }
1152
        return;
1141
        return;

1185
        ));
1174
        ));
1186
        counter -= 1
1175
        counter -= 1
1187
    }
1176
    }
1188
    (*S_).win.as_ref().unwrap().mv(1i32, 0i32);
1177
    (*S_).win.as_ref().unwrap().mv(1i32, 0i32);
1189
    (*S_).win.as_ref().unwrap().addnstr(
1178
    (*S_).win.as_ref().unwrap().addnstr(
1190
        Some("You found kitten! Way to go, robot!\u{0}")
1179
        "You found kitten! Way to go, robot!\u{0}".trim_end_matches('\u{0}'),
1191
            .unwrap()
1192
            .trim_end_matches('\u{0}'),
1193
        -1i32 as usize,
1180
        -1i32 as usize,
1194
    );
1181
    );
1195
    (*S_).win.as_ref().unwrap().refresh();
1182
    (*S_).win.as_ref().unwrap().refresh();
1196
    finish(0i32);
1183
    finish(0i32);
1197
}
1184
}

And second, "foo\0".trim_end_matches('\0') is the same as just "foo". This one is a little more complicated to rewrite. We first remove null terminators throughout the crate, then remove the calls to trim_end_matches:

select target 'crate; desc(expr);' ;
remove_null_terminator ;
clear_marks ;

rewrite_expr "__e.trim_end_matches('\0')" '__e' ;

Diff #76

src/robotfindskitten.rs
211
    pub y: libc::c_int,
211
    pub y: libc::c_int,
212
    pub color: libc::c_int,
212
    pub color: libc::c_int,
213
    pub bold: bool,
213
    pub bold: bool,
214
    pub character: libc::c_char,
214
    pub character: libc::c_char,
215
}
215
}
216
static ver: &'static str = "1.7320508.406\u{0}";
216
static ver: &'static str = "1.7320508.406";
217
217
218
/*Be sure to change MESSAGES when you change the array, or bad things
218
/*Be sure to change MESSAGES when you change the array, or bad things
219
will happen.*/
219
will happen.*/
220
/*Also, take note that robotfindskitten.c and configure.in
220
/*Also, take note that robotfindskitten.c and configure.in
221
currently have the version number hardcoded into them, and they
221
currently have the version number hardcoded into them, and they
222
should reflect MESSAGES. */
222
should reflect MESSAGES. */
223
/* Watch out for fenceposts.*/
223
/* Watch out for fenceposts.*/
224
static messages: [&'static str; 406] = [
224
static messages: [&'static str; 406] = [
225
    "\"I pity the fool who mistakes me for kitten!\", sez Mr. T.\u{0}",
225
    "\"I pity the fool who mistakes me for kitten!\", sez Mr. T.",
226
    "That\'s just an old tin can.\u{0}",
226
    "That\'s just an old tin can.",
227
    "It\'s an altar to the horse god.\u{0}",
227
    "It\'s an altar to the horse god.",
228
    "A box of dancing mechanical pencils. They dance! They sing!\u{0}",
228
    "A box of dancing mechanical pencils. They dance! They sing!",
229
    "It\'s an old Duke Ellington record.\u{0}",
229
    "It\'s an old Duke Ellington record.",

626
    "It\'s your favorite game -- robotfindscatan!\u{0}",
626
    "It\'s your favorite game -- robotfindscatan!",
627
    "Just a man selling an albatross.\u{0}",
627
    "Just a man selling an albatross.",
628
    "The intermission from a 1930s silent movie.\u{0}",
628
    "The intermission from a 1930s silent movie.",
629
    "It\'s an inverted billiard ball!\u{0}",
629
    "It\'s an inverted billiard ball!",
630
    "The spectre of Sherlock Holmes wills you onwards.\u{0}",
630
    "The spectre of Sherlock Holmes wills you onwards.",
631
];
631
];
632
/*
632
/*
633
 *Function definitions
633
 *Function definitions
634
 */
634
 */
635
/*Initialization and setup functions*/
635
/*Initialization and setup functions*/

901
unsafe extern "C" fn initialize_screen(S_: &mut State) {
901
unsafe extern "C" fn initialize_screen(S_: &mut State) {
902
    let mut counter: libc::c_int = 0;
902
    let mut counter: libc::c_int = 0;
903
    fmt_mvprintw(
903
    fmt_mvprintw(
904
        0i32,
904
        0i32,
905
        0i32,
905
        0i32,
906
        format_args!("robotfindskitten v{:}\n\n", unsafe {
906
        format_args!("robotfindskitten v{:}\n\n", unsafe { ver }),
907
            ver.trim_end_matches('\u{0}')
908
        }),
909
        S_,
907
        S_,
910
    );
908
    );
911
    counter = 0i32;
909
    counter = 0i32;
912
    while counter <= (*S_).win.as_ref().unwrap().get_max_x() - 1i32 {
910
    while counter <= (*S_).win.as_ref().unwrap().get_max_x() - 1i32 {
913
        fmt_printw(format_args!("{:}", 95i32 as u8 as char), S_);
911
        fmt_printw(format_args!("{:}", 95i32 as u8 as char), S_);

982
unsafe extern "C" fn instructions(S_: &mut State) {
980
unsafe extern "C" fn instructions(S_: &mut State) {
983
    let mut dummy: libc::c_char = 0;
981
    let mut dummy: libc::c_char = 0;
984
    fmt_mvprintw(
982
    fmt_mvprintw(
985
        0i32,
983
        0i32,
986
        0i32,
984
        0i32,
987
        format_args!("robotfindskitten v{:}\n", unsafe {
985
        format_args!("robotfindskitten v{:}\n", unsafe { ver }),
988
            ver.trim_end_matches('\u{0}')
989
        }),
990
        S_,
986
        S_,
991
    );
987
    );
992
    fmt_printw(
988
    fmt_printw(
993
        format_args!("By the illustrious Leonard Richardson (C) 1997, 2000\n"),
989
        format_args!("By the illustrious Leonard Richardson (C) 1997, 2000\n"),
994
        S_,
990
        S_,

1051
            old_x = (*S_).robot.x;
1047
            old_x = (*S_).robot.x;
1052
            old_y = (*S_).robot.y
1048
            old_y = (*S_).robot.y
1053
        }
1049
        }
1054
        input = ::encode_input((*S_).win.as_ref().unwrap().getch())
1050
        input = ::encode_input((*S_).win.as_ref().unwrap().getch())
1055
    }
1051
    }
1056
    message("Bye!\u{0}".trim_end_matches('\u{0}'), S_);
1052
    message("Bye!", S_);
1057
    (*S_).win.as_ref().unwrap().refresh();
1053
    (*S_).win.as_ref().unwrap().refresh();
1058
    finish(0i32);
1054
    finish(0i32);
1059
}
1055
}
1060
#[no_mangle]
1056
#[no_mangle]
1061
unsafe extern "C" fn message(mut message_0: &str, S_: &mut State) {
1057
unsafe extern "C" fn message(mut message_0: &str, S_: &mut State) {

1102
        }
1098
        }
1103
        260 | 104 | 72 | 2 => check_x -= 1,
1099
        260 | 104 | 72 | 2 => check_x -= 1,
1104
        261 | 108 | 76 | 6 => check_x += 1,
1100
        261 | 108 | 76 | 6 => check_x += 1,
1105
        0 => {}
1101
        0 => {}
1106
        _ => {
1102
        _ => {
1107
            message(
1103
            message("Invalid input: Use direction keys or Esc.", S_);
1108
                "Invalid input: Use direction keys or Esc.\u{0}".trim_end_matches('\u{0}'),
1109
                S_,
1110
            );
1111
            return;
1104
            return;
1112
        }
1105
        }
1113
    }
1106
    }
1114
    if check_y < 3i32
1107
    if check_y < 3i32
1115
        || check_y > (*S_).win.as_ref().unwrap().get_max_y() - 1i32
1108
        || check_y > (*S_).win.as_ref().unwrap().get_max_y() - 1i32

1130
            }
1123
            }
1131
            _ => {
1124
            _ => {
1132
                message(
1125
                message(
1133
                    messages[(*S_).bogus_messages[((*S_).screen[check_x as isize as usize]
1126
                    messages[(*S_).bogus_messages[((*S_).screen[check_x as isize as usize]
1134
                        [check_y as isize as usize]
1127
                        [check_y as isize as usize]
1135
                        - 2i32) as usize] as usize]
1128
                        - 2i32) as usize] as usize],
1136
                        .trim_end_matches('\u{0}'),
1137
                    S_,
1129
                    S_,
1138
                );
1130
                );
1139
            }
1131
            }
1140
        }
1132
        }
1141
        return;
1133
        return;

1173
            1i32 as libc::c_uint as u64,
1165
            1i32 as libc::c_uint as u64,
1174
        ));
1166
        ));
1175
        counter -= 1
1167
        counter -= 1
1176
    }
1168
    }
1177
    (*S_).win.as_ref().unwrap().mv(1i32, 0i32);
1169
    (*S_).win.as_ref().unwrap().mv(1i32, 0i32);
1178
    (*S_).win.as_ref().unwrap().addnstr(
1170
    (*S_)
1179
        "You found kitten! Way to go, robot!\u{0}".trim_end_matches('\u{0}'),
1171
        .win
1180
        -1i32 as usize,
1172
        .as_ref()
1181
    );
1173
        .unwrap()
1174
        .addnstr("You found kitten! Way to go, robot!", -1i32 as usize);
1182
    (*S_).win.as_ref().unwrap().refresh();
1175
    (*S_).win.as_ref().unwrap().refresh();
1183
    finish(0i32);
1176
    finish(0i32);
1184
}
1177
}
1185
unsafe fn main_0(
1178
unsafe fn main_0(
1186
    mut argc: libc::c_int,
1179
    mut argc: libc::c_int,

This indiscriminate use of remove_null_terminator could introduce bugs (including memory unsafety) if the program still contained code that relies on the presence of the null terminator, such as calls to CStr::from_ptr or libc string functions. But previous refactoring steps have already removed all uses of those functions from robotfindskitten, so this transformation is safe.

commit

Final cleanup

At this point, we have removed all the major sources of unsafety from robotfindskitten. We finish the refactoring with an assortment of minor cleanup steps.

We want to remove unnecessary unsafe blocks, but right now every unsafe block is considered unused because they all occur inside unsafe fns. None of these functions actually need to be unsafe at this point, so we mark them safe:

select target 'crate; desc(item && fn);' ;
set_unsafety safe ;
clear_marks ;

Diff #78

src/robotfindskitten.rs
632
/*
632
/*
633
 *Function definitions
633
 *Function definitions
634
 */
634
 */
635
/*Initialization and setup functions*/
635
/*Initialization and setup functions*/
636
#[no_mangle]
636
#[no_mangle]
637
unsafe extern "C" fn initialize_ncurses(S_: &mut State) {
637
extern "C" fn initialize_ncurses(S_: &mut State) {
638
    unsafe { signal(2i32, Some(finish)) };
638
    unsafe { signal(2i32, Some(finish)) };
639
    (*S_).win = Some(::pancurses::initscr());
639
    (*S_).win = Some(::pancurses::initscr());
640
    (*S_).win.as_ref().unwrap().keypad(0 != 1i32);
640
    (*S_).win.as_ref().unwrap().keypad(0 != 1i32);
641
    ::pancurses::nonl();
641
    ::pancurses::nonl();
642
    0;
642
    0;

709
                code
709
                code
710
            }
710
            }
711
        }
711
        }
712
    }
712
    }
713
}
713
}
714
unsafe extern "C" fn finish(mut sig: libc::c_int) {
714
extern "C" fn finish(mut sig: libc::c_int) {
715
    ::pancurses::endwin();
715
    ::pancurses::endwin();
716
    fmt_printf(format_args!(
716
    fmt_printf(format_args!(
717
        "{:}{:}{:}",
717
        "{:}{:}{:}",
718
        27i32 as u8 as char, '(' as i32 as u8 as char, 'B' as i32 as u8 as char
718
        27i32 as u8 as char, '(' as i32 as u8 as char, 'B' as i32 as u8 as char
719
    ));
719
    ));
720
    ::std::process::exit(0i32 as i32);
720
    ::std::process::exit(0i32 as i32);
721
}
721
}
722
#[no_mangle]
722
#[no_mangle]
723
unsafe extern "C" fn initialize_arrays(S_: &mut State) {
723
extern "C" fn initialize_arrays(S_: &mut State) {
724
    let mut counter: libc::c_int = 0;
724
    let mut counter: libc::c_int = 0;
725
    let mut counter2: libc::c_int = 0;
725
    let mut counter2: libc::c_int = 0;
726
    let mut empty: screen_object = screen_object {
726
    let mut empty: screen_object = screen_object {
727
        x: 0,
727
        x: 0,
728
        y: 0,
728
        y: 0,

782
array is bigger than it needs to be, as we don't need to keep track
782
array is bigger than it needs to be, as we don't need to keep track
783
of the first few rows of the screen. But that requires making an
783
of the first few rows of the screen. But that requires making an
784
offset function and using that everywhere. So not right now. */
784
offset function and using that everywhere. So not right now. */
785
785
786
#[no_mangle]
786
#[no_mangle]
787
unsafe extern "C" fn initialize_robot(S_: &mut State) {
787
extern "C" fn initialize_robot(S_: &mut State) {
788
    (*S_).robot.x = (::rand::random::() >> 1) as libc::c_int
788
    (*S_).robot.x = (::rand::random::() >> 1) as libc::c_int
789
        % ((*S_).win.as_ref().unwrap().get_max_x() - 1i32)
789
        % ((*S_).win.as_ref().unwrap().get_max_x() - 1i32)
790
        + 1i32;
790
        + 1i32;
791
    (*S_).robot.y = (::rand::random::() >> 1) as libc::c_int
791
    (*S_).robot.y = (::rand::random::() >> 1) as libc::c_int
792
        % ((*S_).win.as_ref().unwrap().get_max_y() - 1i32 - 3i32 + 1i32)
792
        % ((*S_).win.as_ref().unwrap().get_max_y() - 1i32 - 3i32 + 1i32)

797
    (*S_).screen[(*S_).robot.x as isize as usize][(*S_).robot.y as isize as usize] = 0i32;
797
    (*S_).screen[(*S_).robot.x as isize as usize][(*S_).robot.y as isize as usize] = 0i32;
798
}
798
}
799
/*Global variables. Bite me, it's fun.*/
799
/*Global variables. Bite me, it's fun.*/
800
800
801
#[no_mangle]
801
#[no_mangle]
802
unsafe extern "C" fn initialize_kitten(S_: &mut State) {
802
extern "C" fn initialize_kitten(S_: &mut State) {
803
    loop {
803
    loop {
804
        (*S_).kitten.x = (::rand::random::() >> 1) as libc::c_int
804
        (*S_).kitten.x = (::rand::random::() >> 1) as libc::c_int
805
            % ((*S_).win.as_ref().unwrap().get_max_x() - 1i32)
805
            % ((*S_).win.as_ref().unwrap().get_max_x() - 1i32)
806
            + 1i32;
806
            + 1i32;
807
        (*S_).kitten.y = (::rand::random::() >> 1) as libc::c_int
807
        (*S_).kitten.y = (::rand::random::() >> 1) as libc::c_int

831
        };
831
        };
832
}
832
}
833
833
834
/*Helper functions*/
834
/*Helper functions*/
835
#[no_mangle]
835
#[no_mangle]
836
unsafe extern "C" fn validchar(mut a: libc::c_char) -> libc::c_int {
836
extern "C" fn validchar(mut a: libc::c_char) -> libc::c_int {
837
    match a as libc::c_int {
837
    match a as libc::c_int {
838
        35 | 32 | 127 => return 0i32,
838
        35 | 32 | 127 => return 0i32,
839
        _ => {}
839
        _ => {}
840
    }
840
    }
841
    return 1i32;
841
    return 1i32;
842
}
842
}
843
#[no_mangle]
843
#[no_mangle]
844
unsafe extern "C" fn initialize_bogus(S_: &mut State) {
844
extern "C" fn initialize_bogus(S_: &mut State) {
845
    let mut counter: libc::c_int = 0;
845
    let mut counter: libc::c_int = 0;
846
    let mut index: libc::c_int = 0;
846
    let mut index: libc::c_int = 0;
847
    counter = 0i32;
847
    counter = 0i32;
848
    while counter < (*S_).num_bogus {
848
    while counter < (*S_).num_bogus {
849
        (*S_).bogus[counter as usize].color =
849
        (*S_).bogus[counter as usize].color =

896
        counter += 1
896
        counter += 1
897
    }
897
    }
898
}
898
}
899
899
900
#[no_mangle]
900
#[no_mangle]
901
unsafe extern "C" fn initialize_screen(S_: &mut State) {
901
extern "C" fn initialize_screen(S_: &mut State) {
902
    let mut counter: libc::c_int = 0;
902
    let mut counter: libc::c_int = 0;
903
    fmt_mvprintw(
903
    fmt_mvprintw(
904
        0i32,
904
        0i32,
905
        0i32,
905
        0i32,
906
        format_args!("robotfindskitten v{:}\n\n", unsafe { ver }),
906
        format_args!("robotfindskitten v{:}\n\n", unsafe { ver }),

919
    draw((*S_).kitten, S_);
919
    draw((*S_).kitten, S_);
920
    draw((*S_).robot, S_);
920
    draw((*S_).robot, S_);
921
    (*S_).win.as_ref().unwrap().refresh();
921
    (*S_).win.as_ref().unwrap().refresh();
922
}
922
}
923
#[no_mangle]
923
#[no_mangle]
924
unsafe extern "C" fn draw(mut o: screen_object, S_: &mut State) {
924
extern "C" fn draw(mut o: screen_object, S_: &mut State) {
925
    full_draw(o, 0 != 0i32, S_);
925
    full_draw(o, 0 != 0i32, S_);
926
}
926
}
927
#[no_mangle]
927
#[no_mangle]
928
unsafe extern "C" fn full_draw(mut o: screen_object, mut in_place: bool, S_: &mut State) {
928
extern "C" fn full_draw(mut o: screen_object, mut in_place: bool, S_: &mut State) {
929
    let mut old: attr_t = 0;
929
    let mut old: attr_t = 0;
930
    let mut dummy: libc::c_short = 0;
930
    let mut dummy: libc::c_short = 0;
931
    let mut new: attr_t = 0;
931
    let mut new: attr_t = 0;
932
    if !(stdscr as *const libc::c_void).is_null() {
932
    if !(stdscr as *const libc::c_void).is_null() {
933
        if !(&mut old as *mut attr_t as *const libc::c_void).is_null() {
933
        if !(&mut old as *mut attr_t as *const libc::c_void).is_null() {

975
        .as_ref()
975
        .as_ref()
976
        .unwrap()
976
        .unwrap()
977
        .attrset(old as libc::c_int as ::pancurses::chtype);
977
        .attrset(old as libc::c_int as ::pancurses::chtype);
978
}
978
}
979
#[no_mangle]
979
#[no_mangle]
980
unsafe extern "C" fn instructions(S_: &mut State) {
980
extern "C" fn instructions(S_: &mut State) {
981
    let mut dummy: libc::c_char = 0;
981
    let mut dummy: libc::c_char = 0;
982
    fmt_mvprintw(
982
    fmt_mvprintw(
983
        0i32,
983
        0i32,
984
        0i32,
984
        0i32,
985
        format_args!("robotfindskitten v{:}\n", unsafe { ver }),
985
        format_args!("robotfindskitten v{:}\n", unsafe { ver }),

1021
    (*S_).win.as_ref().unwrap().refresh();
1021
    (*S_).win.as_ref().unwrap().refresh();
1022
    dummy = ::encode_input((*S_).win.as_ref().unwrap().getch()) as libc::c_char;
1022
    dummy = ::encode_input((*S_).win.as_ref().unwrap().getch()) as libc::c_char;
1023
    (*S_).win.as_ref().unwrap().clear();
1023
    (*S_).win.as_ref().unwrap().clear();
1024
}
1024
}
1025
#[no_mangle]
1025
#[no_mangle]
1026
unsafe extern "C" fn draw_in_place(mut o: screen_object, S_: &mut State) {
1026
extern "C" fn draw_in_place(mut o: screen_object, S_: &mut State) {
1027
    full_draw(o, 0 != 1i32, S_);
1027
    full_draw(o, 0 != 1i32, S_);
1028
}
1028
}
1029
/*Game functions*/
1029
/*Game functions*/
1030
#[no_mangle]
1030
#[no_mangle]
1031
unsafe extern "C" fn play_game(S_: &mut State) {
1031
extern "C" fn play_game(S_: &mut State) {
1032
    let mut old_x: libc::c_int = (*S_).robot.x;
1032
    let mut old_x: libc::c_int = (*S_).robot.x;
1033
    let mut old_y: libc::c_int = (*S_).robot.y;
1033
    let mut old_y: libc::c_int = (*S_).robot.y;
1034
    let mut input: libc::c_int = 0;
1034
    let mut input: libc::c_int = 0;
1035
    input = ::encode_input((*S_).win.as_ref().unwrap().getch());
1035
    input = ::encode_input((*S_).win.as_ref().unwrap().getch());
1036
    while input != 27i32 && input != 'q' as i32 && input != 'Q' as i32 {
1036
    while input != 27i32 && input != 'q' as i32 && input != 'Q' as i32 {

1052
    message("Bye!", S_);
1052
    message("Bye!", S_);
1053
    (*S_).win.as_ref().unwrap().refresh();
1053
    (*S_).win.as_ref().unwrap().refresh();
1054
    finish(0i32);
1054
    finish(0i32);
1055
}
1055
}
1056
#[no_mangle]
1056
#[no_mangle]
1057
unsafe extern "C" fn message(mut message_0: &str, S_: &mut State) {
1057
extern "C" fn message(mut message_0: &str, S_: &mut State) {
1058
    (*S_).win.as_ref().unwrap().mv(1i32, 0i32);
1058
    (*S_).win.as_ref().unwrap().mv(1i32, 0i32);
1059
    (*S_).win.as_ref().unwrap().clrtoeol();
1059
    (*S_).win.as_ref().unwrap().clrtoeol();
1060
    fmt_mvprintw(
1060
    fmt_mvprintw(
1061
        1i32,
1061
        1i32,
1062
        0i32,
1062
        0i32,

1069
    );
1069
    );
1070
    (*S_).win.as_ref().unwrap().mv((*S_).robot.y, (*S_).robot.x);
1070
    (*S_).win.as_ref().unwrap().mv((*S_).robot.y, (*S_).robot.x);
1071
    (*S_).win.as_ref().unwrap().refresh();
1071
    (*S_).win.as_ref().unwrap().refresh();
1072
}
1072
}
1073
#[no_mangle]
1073
#[no_mangle]
1074
unsafe extern "C" fn process_input(mut input: libc::c_int, S_: &mut State) {
1074
extern "C" fn process_input(mut input: libc::c_int, S_: &mut State) {
1075
    let mut check_x: libc::c_int = (*S_).robot.x;
1075
    let mut check_x: libc::c_int = (*S_).robot.x;
1076
    let mut check_y: libc::c_int = (*S_).robot.y;
1076
    let mut check_y: libc::c_int = (*S_).robot.y;
1077
    match input {
1077
    match input {
1078
        12 => {
1078
        12 => {
1079
            (*S_).win.as_ref().unwrap().refresh();
1079
            (*S_).win.as_ref().unwrap().refresh();

1134
    }
1134
    }
1135
    (*S_).robot.x = check_x;
1135
    (*S_).robot.x = check_x;
1136
    (*S_).robot.y = check_y;
1136
    (*S_).robot.y = check_y;
1137
}
1137
}
1138
#[no_mangle]
1138
#[no_mangle]
1139
unsafe extern "C" fn play_animation(mut input: libc::c_int, S_: &mut State) {
1139
extern "C" fn play_animation(mut input: libc::c_int, S_: &mut State) {
1140
    let mut counter: libc::c_int = 0;
1140
    let mut counter: libc::c_int = 0;
1141
    counter = 4i32;
1141
    counter = 4i32;
1142
    while counter > 0i32 {
1142
    while counter > 0i32 {
1143
        if (*S_).win.as_ref().unwrap().mv(1i32, 50i32 + counter + 1i32) == -1i32 {
1143
        if (*S_).win.as_ref().unwrap().mv(1i32, 50i32 + counter + 1i32) == -1i32 {
1144
        } else {
1144
        } else {

1173
        .unwrap()
1173
        .unwrap()
1174
        .addnstr("You found kitten! Way to go, robot!", -1i32 as usize);
1174
        .addnstr("You found kitten! Way to go, robot!", -1i32 as usize);
1175
    (*S_).win.as_ref().unwrap().refresh();
1175
    (*S_).win.as_ref().unwrap().refresh();
1176
    finish(0i32);
1176
    finish(0i32);
1177
}
1177
}
1178
unsafe fn main_0(
1178
fn main_0(
1179
    mut argc: libc::c_int,
1179
    mut argc: libc::c_int,
1180
    mut argv: ::c2rust_runtime::CArray<Option<&::std::ffi::CStr>>,
1180
    mut argv: ::c2rust_runtime::CArray<Option<&::std::ffi::CStr>>,
1181
) -> libc::c_int {
1181
) -> libc::c_int {
1182
    let mut S: State = State {
1182
    let mut S: State = State {
1183
        win: None,
1183
        win: None,

This part can't be fully automated. In general, there is no easy way to tell whether the safety of a given function relies on unchecked assumptions about its input, or whether it might break invariants that other functions rely on. In the case of robotfindskitten, every function really is safe, but for other applications or libraries, it might be necessary to be more selective when removing the unsafe qualifier.

Now that all functions are safe, fix_unused_unsafe will remove any unsafe blocks that contain no unsafe operations:

fix_unused_unsafe

Diff #79

src/robotfindskitten.rs
105
    y: libc::c_int,
105
    y: libc::c_int,
106
    x: libc::c_int,
106
    x: libc::c_int,
107
    args: ::std::fmt::Arguments,
107
    args: ::std::fmt::Arguments,
108
    S_: &mut State,
108
    S_: &mut State,
109
) -> libc::c_int {
109
) -> libc::c_int {
110
    unsafe {
110
    {
111
        (*S_)
111
        (*S_)
112
            .win
112
            .win
113
            .as_ref()
113
            .as_ref()
114
            .unwrap()
114
            .unwrap()
115
            .mvprintw(y, x, &format!("{}", args))
115
            .mvprintw(y, x, &format!("{}", args))
116
    }
116
    }
117
}
117
}
118
fn fmt_printw(args: ::std::fmt::Arguments, S_: &mut State) -> libc::c_int {
118
fn fmt_printw(args: ::std::fmt::Arguments, S_: &mut State) -> libc::c_int {
119
    {
119
    unsafe { (*S_).win.as_ref().unwrap().printw(&format!("{}", args)) }
120
        (*S_).win.as_ref().unwrap().printw(&format!("{}", args))
121
    }
120
}
122
}
121
fn fmt_printf(args: ::std::fmt::Arguments) -> libc::c_int {
123
fn fmt_printf(args: ::std::fmt::Arguments) -> libc::c_int {
122
    print!("{}", args);
124
    print!("{}", args);
123
    0
125
    0
124
}
126
}

729
        color: 0,
731
        color: 0,
730
        bold: false,
732
        bold: false,
731
        character: 0,
733
        character: 0,
732
    };
734
    };
733
    let mut i: libc::c_int = 0i32;
735
    let mut i: libc::c_int = 0i32;
734
    (*S_).screen = unsafe {
736
    (*S_).screen = {
735
        ::c2rust_runtime::CArray::alloc(
737
        ::c2rust_runtime::CArray::alloc(
736
            (::std::mem::size_of::<*mut libc::c_int>() as libc::c_ulong).wrapping_mul(
738
            (::std::mem::size_of::<*mut libc::c_int>() as libc::c_ulong).wrapping_mul(
737
                ((*S_).win.as_ref().unwrap().get_max_x() - 1i32 + 1i32) as libc::c_ulong,
739
                ((*S_).win.as_ref().unwrap().get_max_x() - 1i32 + 1i32) as libc::c_ulong,
738
            ) as usize
740
            ) as usize
739
                / ::std::mem::size_of::<::c2rust_runtime::CArray<i32>>(),
741
                / ::std::mem::size_of::<::c2rust_runtime::CArray<i32>>(),
740
        )
742
        )
741
    };
743
    };
742
    i = 0i32;
744
    i = 0i32;
743
    while i < (*S_).win.as_ref().unwrap().get_max_x() - 1i32 + 1i32 {
745
    while i < (*S_).win.as_ref().unwrap().get_max_x() - 1i32 + 1i32 {
744
        let ref mut fresh0 = (*S_).screen[i as isize as usize];
746
        let ref mut fresh0 = (*S_).screen[i as isize as usize];
745
        *fresh0 = unsafe {
747
        *fresh0 = {
746
            ::c2rust_runtime::CArray::alloc(
748
            ::c2rust_runtime::CArray::alloc(
747
                (::std::mem::size_of::() as libc::c_ulong).wrapping_mul(
749
                (::std::mem::size_of::() as libc::c_ulong).wrapping_mul(
748
                    ((*S_).win.as_ref().unwrap().get_max_y() - 1i32 + 1i32) as libc::c_ulong,
750
                    ((*S_).win.as_ref().unwrap().get_max_y() - 1i32 + 1i32) as libc::c_ulong,
749
                ) as usize
751
                ) as usize
750
                    / ::std::mem::size_of::(),
752
                    / ::std::mem::size_of::(),

901
extern "C" fn initialize_screen(S_: &mut State) {
903
extern "C" fn initialize_screen(S_: &mut State) {
902
    let mut counter: libc::c_int = 0;
904
    let mut counter: libc::c_int = 0;
903
    fmt_mvprintw(
905
    fmt_mvprintw(
904
        0i32,
906
        0i32,
905
        0i32,
907
        0i32,
906
        format_args!("robotfindskitten v{:}\n\n", unsafe { ver }),
908
        format_args!("robotfindskitten v{:}\n\n", { ver }),
907
        S_,
909
        S_,
908
    );
910
    );
909
    counter = 0i32;
911
    counter = 0i32;
910
    while counter <= (*S_).win.as_ref().unwrap().get_max_x() - 1i32 {
912
    while counter <= (*S_).win.as_ref().unwrap().get_max_x() - 1i32 {
911
        fmt_printw(format_args!("{:}", 95i32 as u8 as char), S_);
913
        fmt_printw(format_args!("{:}", 95i32 as u8 as char), S_);

980
extern "C" fn instructions(S_: &mut State) {
982
extern "C" fn instructions(S_: &mut State) {
981
    let mut dummy: libc::c_char = 0;
983
    let mut dummy: libc::c_char = 0;
982
    fmt_mvprintw(
984
    fmt_mvprintw(
983
        0i32,
985
        0i32,
984
        0i32,
986
        0i32,
985
        format_args!("robotfindskitten v{:}\n", unsafe { ver }),
987
        format_args!("robotfindskitten v{:}\n", { ver }),
986
        S_,
988
        S_,
987
    );
989
    );
988
    fmt_printw(
990
    fmt_printw(
989
        format_args!("By the illustrious Leonard Richardson (C) 1997, 2000\n"),
991
        format_args!("By the illustrious Leonard Richardson (C) 1997, 2000\n"),
990
        S_,
992
        S_,

1058
    (*S_).win.as_ref().unwrap().mv(1i32, 0i32);
1060
    (*S_).win.as_ref().unwrap().mv(1i32, 0i32);
1059
    (*S_).win.as_ref().unwrap().clrtoeol();
1061
    (*S_).win.as_ref().unwrap().clrtoeol();
1060
    fmt_mvprintw(
1062
    fmt_mvprintw(
1061
        1i32,
1063
        1i32,
1062
        0i32,
1064
        0i32,
1063
        format_args!(
1064
            "{:.*}",
1065
            (*S_).win.as_ref().unwrap().get_max_x() as usize,
1065
        format_args!("{:.*}", (*S_).win.as_ref().unwrap().get_max_x() as usize, {
1066
            unsafe { &message_0.to_owned() }
1066
            &message_0.to_owned()
1067
        ),
1067
        }),
1068
        S_,
1068
        S_,
1069
    );
1069
    );
1070
    (*S_).win.as_ref().unwrap().mv((*S_).robot.y, (*S_).robot.x);
1070
    (*S_).win.as_ref().unwrap().mv((*S_).robot.y, (*S_).robot.x);
1071
    (*S_).win.as_ref().unwrap().refresh();
1071
    (*S_).win.as_ref().unwrap().refresh();
1072
}
1072
}

1188
            bold: false,
1188
            bold: false,
1189
            character: 0,
1189
            character: 0,
1190
        }; 406],
1190
        }; 406],
1191
        bogus_messages: [0; 406],
1191
        bogus_messages: [0; 406],
1192
        used_messages: [0; 406],
1192
        used_messages: [0; 406],
1193
        screen: unsafe { ::c2rust_runtime::CArray::empty() },
1193
        screen: { ::c2rust_runtime::CArray::empty() },
1194
        robot: screen_object {
1194
        robot: screen_object {
1195
            x: 0,
1195
            x: 0,
1196
            y: 0,
1196
            y: 0,
1197
            color: 0,
1197
            color: 0,
1198
            bold: false,
1198
            bold: false,

1256
    for i in 0..args_owned.len() {
1256
    for i in 0..args_owned.len() {
1257
        args[i] = Some(&args_owned[i]);
1257
        args[i] = Some(&args_owned[i]);
1258
    }
1258
    }
1259
    // The last element of `args` remains `None`.
1259
    // The last element of `args` remains `None`.
1260
1260
1261
    unsafe {
1261
    {
1262
        ::std::process::exit(main_0((args.len() - 1) as libc::c_int, args) as i32);
1262
        ::std::process::exit(main_0((args.len() - 1) as libc::c_int, args) as i32);
1263
    }
1263
    }
1264
}
1264
}
1265
fn opt_c_str_to_ptr(x: Option<&::std::ffi::CStr>) -> *const libc::c_char {
1265
fn opt_c_str_to_ptr(x: Option<&::std::ffi::CStr>) -> *const libc::c_char {
1266
    match x {
1266
    match x {

Next, we remove a number of unused items from the crate. We have replaced all uses of the FFI declarations generated by c2rust with alternatives, except for one call to signal. We generate a new extern "C" block containing only the declaration of signal, then delete the old unused extern "C" blocks. remove the declarations now:

select target 'crate; desc(foreign_mod); last;' ;
create_item '
    extern "C" {
        fn signal(sig: libc::c_int, handler: __sighandler_t) -> __sighandler_t;
    }
' after ;

select target 'crate; desc(foreign_mod && !marked(new));' ;
delete_items ;

Diff #80

src/robotfindskitten.rs
20
}
20
}
21
extern crate c2rust_runtime;
21
extern crate c2rust_runtime;
22
extern crate libc;
22
extern crate libc;
23
extern crate pancurses;
23
extern crate pancurses;
24
extern "C" {
24
extern "C" {
25
    pub type ldat;
26
    #[no_mangle]
27
    fn printf(_: *const libc::c_char, ...) -> libc::c_int;
28
    #[no_mangle]
29
    fn cbreak() -> libc::c_int;
30
    #[no_mangle]
31
    fn endwin() -> libc::c_int;
32
    #[no_mangle]
33
    fn has_colors() -> bool;
34
    #[no_mangle]
35
    fn initscr() -> *mut WINDOW;
36
    #[no_mangle]
37
    fn init_pair(_: libc::c_short, _: libc::c_short, _: libc::c_short) -> libc::c_int;
38
    #[no_mangle]
39
    fn intrflush(_: *mut WINDOW, _: bool) -> libc::c_int;
40
    #[no_mangle]
41
    fn keypad(_: *mut WINDOW, _: bool) -> libc::c_int;
42
    #[no_mangle]
43
    fn mvprintw(_: libc::c_int, _: libc::c_int, _: *const libc::c_char, ...) -> libc::c_int;
44
    #[no_mangle]
45
    fn noecho() -> libc::c_int;
46
    #[no_mangle]
47
    fn nonl() -> libc::c_int;
48
    #[no_mangle]
49
    fn printw(_: *const libc::c_char, ...) -> libc::c_int;
50
    #[no_mangle]
51
    fn start_color() -> libc::c_int;
52
    #[no_mangle]
53
    fn waddch(_: *mut WINDOW, _: chtype) -> libc::c_int;
54
    #[no_mangle]
55
    fn waddnstr(_: *mut WINDOW, _: *const libc::c_char, _: libc::c_int) -> libc::c_int;
56
    #[no_mangle]
57
    fn wclear(_: *mut WINDOW) -> libc::c_int;
58
    #[no_mangle]
59
    fn wclrtoeol(_: *mut WINDOW) -> libc::c_int;
60
    #[no_mangle]
61
    fn wgetch(_: *mut WINDOW) -> libc::c_int;
62
    #[no_mangle]
63
    fn wmove(_: *mut WINDOW, _: libc::c_int, _: libc::c_int) -> libc::c_int;
64
    #[no_mangle]
65
    fn wrefresh(_: *mut WINDOW) -> libc::c_int;
66
    #[no_mangle]
67
    static mut curscr: *mut WINDOW;
68
    #[no_mangle]
69
    static mut stdscr: *mut WINDOW;
70
    #[no_mangle]
71
    static mut COLS: libc::c_int;
72
    #[no_mangle]
73
    static mut LINES: libc::c_int;
74
    #[no_mangle]
75
    fn signal(__sig: libc::c_int, __handler: __sighandler_t) -> __sighandler_t;
25
    fn signal(sig: libc::c_int, handler: __sighandler_t) -> __sighandler_t;
76
    #[no_mangle]
77
    fn strtol(
78
        __nptr: *const libc::c_char,
79
        __endptr: *mut *mut libc::c_char,
80
        __base: libc::c_int,
81
    ) -> libc::c_long;
82
    #[no_mangle]
83
    fn rand() -> libc::c_int;
84
    #[no_mangle]
85
    fn srand(__seed: libc::c_uint);
86
    #[no_mangle]
87
    fn malloc(_: libc::c_ulong) -> *mut libc::c_void;
88
    #[no_mangle]
89
    fn exit(_: libc::c_int) -> !;
90
    #[no_mangle]
91
    fn time(__timer: *mut time_t) -> time_t;
92
    #[no_mangle]
93
    fn sleep(__seconds: libc::c_uint) -> libc::c_uint;
94
}
26
}
95
extern "C" {
96
    fn wattr_get(
97
        win: *mut WINDOW,
98
        attrs: *mut attr_t,
99
        pair: *mut libc::c_short,
100
        opts: *mut libc::c_void,
101
    ) -> libc::c_int;
102
    fn wattrset(win: *mut WINDOW, attrs: libc::c_int) -> libc::c_int;
103
}
104
fn fmt_mvprintw(
27
fn fmt_mvprintw(
105
    y: libc::c_int,
28
    y: libc::c_int,
106
    x: libc::c_int,
29
    x: libc::c_int,
107
    args: ::std::fmt::Arguments,
30
    args: ::std::fmt::Arguments,
108
    S_: &mut State,
31
    S_: &mut State,

Furthermore, we can delete a number of type declarations that were previously used only in foreign functions:

select target '
    item(__time_t);
    item(time_t);
    item(pdat);
    item(_win_st);
    item(WINDOW);
' ;
delete_items ;

Diff #81

src/robotfindskitten.rs
45
}
45
}
46
fn fmt_printf(args: ::std::fmt::Arguments) -> libc::c_int {
46
fn fmt_printf(args: ::std::fmt::Arguments) -> libc::c_int {
47
    print!("{}", args);
47
    print!("{}", args);
48
    0
48
    0
49
}
49
}
50
pub type __time_t = libc::c_long;
50
51
pub type chtype = libc::c_ulong;
51
pub type chtype = libc::c_ulong;
52
52
53
#[repr(C)]
54
#[derive(Copy, Clone)]
55
pub struct _win_st {
56
    pub _cury: libc::c_short,
57
    pub _curx: libc::c_short,
58
    pub _maxy: libc::c_short,
59
    pub _maxx: libc::c_short,
60
    pub _begy: libc::c_short,
61
    pub _begx: libc::c_short,
62
    pub _flags: libc::c_short,
63
    pub _attrs: attr_t,
64
    pub _bkgd: chtype,
65
    pub _notimeout: bool,
66
    pub _clear: bool,
67
    pub _leaveok: bool,
68
    pub _scroll: bool,
69
    pub _idlok: bool,
70
    pub _idcok: bool,
71
    pub _immed: bool,
72
    pub _sync: bool,
73
    pub _use_keypad: bool,
74
    pub _delay: libc::c_int,
75
    pub _line: *mut ldat,
76
    pub _regtop: libc::c_short,
77
    pub _regbottom: libc::c_short,
78
    pub _parx: libc::c_int,
79
    pub _pary: libc::c_int,
80
    pub _parent: *mut WINDOW,
81
    pub _pad: pdat,
82
    pub _yoffset: libc::c_short,
83
}
84
85
#[repr(C)]
86
#[derive(Copy, Clone)]
87
pub struct pdat {
88
    pub _pad_y: libc::c_short,
89
    pub _pad_x: libc::c_short,
90
    pub _pad_top: libc::c_short,
91
    pub _pad_left: libc::c_short,
92
    pub _pad_bottom: libc::c_short,
93
    pub _pad_right: libc::c_short,
94
}
95
pub type WINDOW = _win_st;
96
pub type attr_t = chtype;
53
pub type attr_t = chtype;
97
pub type __sighandler_t = Option<unsafe extern "C" fn(_: libc::c_int) -> ()>;
54
pub type __sighandler_t = Option<unsafe extern "C" fn(_: libc::c_int) -> ()>;
98
pub type time_t = __time_t;
55
99
/*
56
/*
100
 *robotfindskitten: A Zen simulation
57
 *robotfindskitten: A Zen simulation
101
 *
58
 *
102
 *Copyright (C) 1997,2000 Leonard Richardson
59
 *Copyright (C) 1997,2000 Leonard Richardson
103
 *                        leonardr@segfault.org
60
 *                        leonardr@segfault.org

Similarly, we can delete the opt_c_str_to_ptr helper function, which we used only temporarily while cleaning up string-pointer function arguments:

select target '
    item(opt_c_str_to_ptr);
' ;
delete_items ;

Diff #82

src/robotfindskitten.rs
1140
1140
1141
    {
1141
    {
1142
        ::std::process::exit(main_0((args.len() - 1) as libc::c_int, args) as i32);
1142
        ::std::process::exit(main_0((args.len() - 1) as libc::c_int, args) as i32);
1143
    }
1143
    }
1144
}
1144
}
1145
fn opt_c_str_to_ptr(x: Option<&::std::ffi::CStr>) -> *const libc::c_char {
1146
    match x {
1147
        None => ::std::ptr::null(),
1148
        Some(x) => x.as_ptr(),
1149
    }
1150
}

Now we are done refactoring robotfindskitten. We have preserved the functionality of the original C program, but all unsafe code has been removed, with the exception of a single signal call that cannot be made safe. The refactored Rust version of robotfindskitten is still unidiomatic and somewhat difficult to read, but by removing nearly all of the unsafe code, we have established a solid foundation for future improvements.

Final output

Here is the final refactored version of robotfindskitten:

Diff #83

src/robotfindskitten.rs
1
#![allow(
2
    dead_code,
3
    mutable_transmutes,
4
    non_camel_case_types,
5
    non_snake_case,
6
    non_upper_case_globals,
7
    unused_mut
8
)]
9
#![feature(const_raw_ptr_to_usize_cast, extern_types, libc)]
10
extern crate rand;
11
struct State {
12
    win: Option<::pancurses::Window>,
13
    bogus: [screen_object; 406],
14
    bogus_messages: [libc::c_int; 406],
15
    used_messages: [libc::c_int; 406],
16
    screen: ::c2rust_runtime::CArray<::c2rust_runtime::CArray>,
17
    robot: screen_object,
18
    kitten: screen_object,
19
    num_bogus: libc::c_int,
20
}
21
extern crate c2rust_runtime;
22
extern crate libc;
23
extern crate pancurses;
24
extern "C" {
25
    fn signal(sig: libc::c_int, handler: __sighandler_t) -> __sighandler_t;
26
}
27
fn fmt_mvprintw(
28
    y: libc::c_int,
29
    x: libc::c_int,
30
    args: ::std::fmt::Arguments,
31
    S_: &mut State,
32
) -> libc::c_int {
33
    {
34
        (*S_)
35
            .win
36
            .as_ref()
37
            .unwrap()
38
            .mvprintw(y, x, &format!("{}", args))
39
    }
40
}
41
fn fmt_printw(args: ::std::fmt::Arguments, S_: &mut State) -> libc::c_int {
42
    {
43
        (*S_).win.as_ref().unwrap().printw(&format!("{}", args))
44
    }
45
}
46
fn fmt_printf(args: ::std::fmt::Arguments) -> libc::c_int {
47
    print!("{}", args);
48
    0
49
}
50
51
pub type chtype = libc::c_ulong;
52
53
pub type attr_t = chtype;
54
pub type __sighandler_t = Option<unsafe extern "C" fn(_: libc::c_int) -> ()>;
55
56
/*
57
 *robotfindskitten: A Zen simulation
58
 *
59
 *Copyright (C) 1997,2000 Leonard Richardson
60
 *                        leonardr@segfault.org
61
 *                        http://www.crummy.com/devel/
62
 *
63
 *   This program is free software; you can redistribute it and/or
64
 *   modify it under the terms of the GNU General Public License as
65
 *   published by the Free Software Foundation; either version 2 of
66
 *   the License, or (at your option) any later version.
67
 *
68
 *   This program is distributed in the hope that it will be useful,
69
 *   but WITHOUT ANY WARRANTY; without even the implied warranty of
70
 *   MERCHANTABILITY or EXISTANCE OF KITTEN.  See the GNU General
71
 *   Public License for more details.
72
 *
73
 *   http://www.gnu.org/copyleft/gpl.html
74
 *
75
 */
76
/*The messages go in a separate file because they are collectively
77
huge, and you might want to modify them. It would be nice to load
78
the messages from a text file at run time.*/
79
/*Constants for our internal representation of the screen.*/
80
/*Keycode constants*/
81
/*Nethack keycodes*/
82
/*EMACS keycodes - Subtracting 64 makes it a control command*/
83
/*Screen dimensions.*/
84
/*Macros for generating numbers in different ranges*/
85
/*Row constants for the animation*/
86
/*This struct contains all the information we need to display an object
87
on the screen*/
88
89
#[repr(C)]
90
#[derive(Copy, Clone)]
91
pub struct screen_object {
92
    pub x: libc::c_int,
93
    pub y: libc::c_int,
94
    pub color: libc::c_int,
95
    pub bold: bool,
96
    pub character: libc::c_char,
97
}
98
static ver: &'static str = "1.7320508.406";
99
100
/*Be sure to change MESSAGES when you change the array, or bad things
101
will happen.*/
102
/*Also, take note that robotfindskitten.c and configure.in
103
currently have the version number hardcoded into them, and they
104
should reflect MESSAGES. */
105
/* Watch out for fenceposts.*/
106
static messages: [&'static str; 406] = [
107
    "\"I pity the fool who mistakes me for kitten!\", sez Mr. T.",
108
    "That\'s just an old tin can.",
109
    "It\'s an altar to the horse god.",
110
    "A box of dancing mechanical pencils. They dance! They sing!",
111
    "It\'s an old Duke Ellington record.",

508
    "It\'s your favorite game -- robotfindscatan!",
509
    "Just a man selling an albatross.",
510
    "The intermission from a 1930s silent movie.",
511
    "It\'s an inverted billiard ball!",
512
    "The spectre of Sherlock Holmes wills you onwards.",
513
];
514
/*
515
 *Function definitions
516
 */
517
/*Initialization and setup functions*/
518
#[no_mangle]
519
extern "C" fn initialize_ncurses(S_: &mut State) {
520
    unsafe { signal(2i32, Some(finish)) };
521
    (*S_).win = Some(::pancurses::initscr());
522
    (*S_).win.as_ref().unwrap().keypad(0 != 1i32);
523
    ::pancurses::nonl();
524
    0;
525
    ::pancurses::noecho();
526
    ::pancurses::cbreak();
527
    if ::pancurses::has_colors() {
528
        ::pancurses::start_color();
529
        ::pancurses::init_pair(
530
            0i32 as libc::c_short,
531
            0i32 as libc::c_short,
532
            0i32 as libc::c_short,
533
        );
534
        ::pancurses::init_pair(
535
            2i32 as libc::c_short,
536
            2i32 as libc::c_short,
537
            0i32 as libc::c_short,
538
        );
539
        ::pancurses::init_pair(
540
            1i32 as libc::c_short,
541
            1i32 as libc::c_short,
542
            0i32 as libc::c_short,
543
        );
544
        ::pancurses::init_pair(
545
            6i32 as libc::c_short,
546
            6i32 as libc::c_short,
547
            0i32 as libc::c_short,
548
        );
549
        ::pancurses::init_pair(
550
            7i32 as libc::c_short,
551
            7i32 as libc::c_short,
552
            0i32 as libc::c_short,
553
        );
554
        ::pancurses::init_pair(
555
            5i32 as libc::c_short,
556
            5i32 as libc::c_short,
557
            0i32 as libc::c_short,
558
        );
559
        ::pancurses::init_pair(
560
            4i32 as libc::c_short,
561
            4i32 as libc::c_short,
562
            0i32 as libc::c_short,
563
        );
564
        ::pancurses::init_pair(
565
            3i32 as libc::c_short,
566
            3i32 as libc::c_short,
567
            0i32 as libc::c_short,
568
        );
569
    };
570
}
571
fn encode_input(inp: Option<::pancurses::Input>) -> libc::c_int {
572
    use pancurses::Input::*;
573
    let inp = match inp {
574
        Some(x) => x,
575
        None => return -1,
576
    };
577
    match inp {
578
        // TODO: unicode inputs in the range 256 .. 512 can
579
        // collide with ncurses special keycodes
580
        Character(c) => c as u32 as libc::c_int,
581
        Unknown(i) => i,
582
        special => {
583
            let idx = ::pancurses::SPECIAL_KEY_CODES
584
                .iter()
585
                .position(|&k| k == special)
586
                .unwrap();
587
            let code = idx as i32 + ::pancurses::KEY_OFFSET;
588
            if code > ::pancurses::KEY_F15 {
589
                code + 48
590
            } else {
591
                code
592
            }
593
        }
594
    }
595
}
596
extern "C" fn finish(mut sig: libc::c_int) {
597
    ::pancurses::endwin();
598
    fmt_printf(format_args!(
599
        "{:}{:}{:}",
600
        27i32 as u8 as char, '(' as i32 as u8 as char, 'B' as i32 as u8 as char
601
    ));
602
    ::std::process::exit(0i32 as i32);
603
}
604
#[no_mangle]
605
extern "C" fn initialize_arrays(S_: &mut State) {
606
    let mut counter: libc::c_int = 0;
607
    let mut counter2: libc::c_int = 0;
608
    let mut empty: screen_object = screen_object {
609
        x: 0,
610
        y: 0,
611
        color: 0,
612
        bold: false,
613
        character: 0,
614
    };
615
    let mut i: libc::c_int = 0i32;
616
    (*S_).screen = {
617
        ::c2rust_runtime::CArray::alloc(
618
            (::std::mem::size_of::<*mut libc::c_int>() as libc::c_ulong).wrapping_mul(
619
                ((*S_).win.as_ref().unwrap().get_max_x() - 1i32 + 1i32) as libc::c_ulong,
620
            ) as usize
621
                / ::std::mem::size_of::<::c2rust_runtime::CArray<i32>>(),
622
        )
623
    };
624
    i = 0i32;
625
    while i < (*S_).win.as_ref().unwrap().get_max_x() - 1i32 + 1i32 {
626
        let ref mut fresh0 = (*S_).screen[i as isize as usize];
627
        *fresh0 = {
628
            ::c2rust_runtime::CArray::alloc(
629
                (::std::mem::size_of::() as libc::c_ulong).wrapping_mul(
630
                    ((*S_).win.as_ref().unwrap().get_max_y() - 1i32 + 1i32) as libc::c_ulong,
631
                ) as usize
632
                    / ::std::mem::size_of::(),
633
            )
634
        };
635
        i += 1
636
    }
637
    empty.x = -1i32;
638
    empty.y = -1i32;
639
    empty.color = 0i32;
640
    empty.bold = 0 != 0i32;
641
    empty.character = ' ' as i32 as libc::c_char;
642
    counter = 0i32;
643
    while counter <= (*S_).win.as_ref().unwrap().get_max_x() - 1i32 {
644
        counter2 = 0i32;
645
        while counter2 <= (*S_).win.as_ref().unwrap().get_max_y() - 1i32 {
646
            (*S_).screen[counter as isize as usize][counter2 as isize as usize] = -1i32;
647
            counter2 += 1
648
        }
649
        counter += 1
650
    }
651
    counter = 0i32;
652
    while (counter as libc::c_ulong)
653
        < (::std::mem::size_of::<[*mut libc::c_char; 406]>() as libc::c_ulong)
654
            .wrapping_div(::std::mem::size_of::<*mut libc::c_char>() as libc::c_ulong)
655
    {
656
        (*S_).used_messages[counter as usize] = 0i32;
657
        (*S_).bogus_messages[counter as usize] = 0i32;
658
        (*S_).bogus[counter as usize] = empty;
659
        counter += 1
660
    }
661
}
662
663
/* This array contains our internal representation of the screen. The
664
array is bigger than it needs to be, as we don't need to keep track
665
of the first few rows of the screen. But that requires making an
666
offset function and using that everywhere. So not right now. */
667
668
#[no_mangle]
669
extern "C" fn initialize_robot(S_: &mut State) {
670
    (*S_).robot.x = (::rand::random::() >> 1) as libc::c_int
671
        % ((*S_).win.as_ref().unwrap().get_max_x() - 1i32)
672
        + 1i32;
673
    (*S_).robot.y = (::rand::random::() >> 1) as libc::c_int
674
        % ((*S_).win.as_ref().unwrap().get_max_y() - 1i32 - 3i32 + 1i32)
675
        + 3i32;
676
    (*S_).robot.character = '#' as i32 as libc::c_char;
677
    (*S_).robot.color = 0i32;
678
    (*S_).robot.bold = 0 != 0i32;
679
    (*S_).screen[(*S_).robot.x as isize as usize][(*S_).robot.y as isize as usize] = 0i32;
680
}
681
/*Global variables. Bite me, it's fun.*/
682
683
#[no_mangle]
684
extern "C" fn initialize_kitten(S_: &mut State) {
685
    loop {
686
        (*S_).kitten.x = (::rand::random::() >> 1) as libc::c_int
687
            % ((*S_).win.as_ref().unwrap().get_max_x() - 1i32)
688
            + 1i32;
689
        (*S_).kitten.y = (::rand::random::() >> 1) as libc::c_int
690
            % ((*S_).win.as_ref().unwrap().get_max_y() - 1i32 - 3i32 + 1i32)
691
            + 3i32;
692
        if !((*S_).screen[(*S_).kitten.x as isize as usize][(*S_).kitten.y as isize as usize]
693
            != -1i32)
694
        {
695
            break;
696
        }
697
    }
698
    loop {
699
        (*S_).kitten.character = ((::rand::random::() >> 1) as libc::c_int
700
            % (126i32 - '!' as i32 + 1i32)
701
            + '!' as i32) as libc::c_char;
702
        if !(0 == validchar((*S_).kitten.character)) {
703
            break;
704
        }
705
    }
706
    (*S_).screen[(*S_).kitten.x as isize as usize][(*S_).kitten.y as isize as usize] = 1i32;
707
    (*S_).kitten.color = (::rand::random::() >> 1) as libc::c_int % 6i32 + 1i32;
708
    (*S_).kitten.bold = 0
709
        != if 0 != (::rand::random::() >> 1) as libc::c_int % 2i32 {
710
            1i32
711
        } else {
712
            0i32
713
        };
714
}
715
716
/*Helper functions*/
717
#[no_mangle]
718
extern "C" fn validchar(mut a: libc::c_char) -> libc::c_int {
719
    match a as libc::c_int {
720
        35 | 32 | 127 => return 0i32,
721
        _ => {}
722
    }
723
    return 1i32;
724
}
725
#[no_mangle]
726
extern "C" fn initialize_bogus(S_: &mut State) {
727
    let mut counter: libc::c_int = 0;
728
    let mut index: libc::c_int = 0;
729
    counter = 0i32;
730
    while counter < (*S_).num_bogus {
731
        (*S_).bogus[counter as usize].color =
732
            (::rand::random::() >> 1) as libc::c_int % 6i32 + 1i32;
733
        (*S_).bogus[counter as usize].bold = 0
734
            != if 0 != (::rand::random::() >> 1) as libc::c_int % 2i32 {
735
                1i32
736
            } else {
737
                0i32
738
            };
739
        loop {
740
            (*S_).bogus[counter as usize].character = ((::rand::random::() >> 1)
741
                as libc::c_int
742
                % (126i32 - '!' as i32 + 1i32)
743
                + '!' as i32) as libc::c_char;
744
            if !(0 == validchar((*S_).bogus[counter as usize].character)) {
745
                break;
746
            }
747
        }
748
        loop {
749
            (*S_).bogus[counter as usize].x = (::rand::random::() >> 1)
750
                as libc::c_int
751
                % ((*S_).win.as_ref().unwrap().get_max_x() - 1i32)
752
                + 1i32;
753
            (*S_).bogus[counter as usize].y = (::rand::random::() >> 1)
754
                as libc::c_int
755
                % ((*S_).win.as_ref().unwrap().get_max_y() - 1i32 - 3i32 + 1i32)
756
                + 3i32;
757
            if !((*S_).screen[(*S_).bogus[counter as usize].x as isize as usize]
758
                [(*S_).bogus[counter as usize].y as isize as usize]
759
                != -1i32)
760
            {
761
                break;
762
            }
763
        }
764
        (*S_).screen[(*S_).bogus[counter as usize].x as isize as usize]
765
            [(*S_).bogus[counter as usize].y as isize as usize] = counter + 2i32;
766
        loop {
767
            index = ((::rand::random::() >> 1) as libc::c_int as libc::c_ulong)
768
                .wrapping_rem(
769
                    (::std::mem::size_of::<[*mut libc::c_char; 406]>() as libc::c_ulong)
770
                        .wrapping_div(::std::mem::size_of::<*mut libc::c_char>() as libc::c_ulong),
771
                ) as libc::c_int;
772
            if !((*S_).used_messages[index as usize] != 0i32) {
773
                break;
774
            }
775
        }
776
        (*S_).bogus_messages[counter as usize] = index;
777
        (*S_).used_messages[index as usize] = 1i32;
778
        counter += 1
779
    }
780
}
781
782
#[no_mangle]
783
extern "C" fn initialize_screen(S_: &mut State) {
784
    let mut counter: libc::c_int = 0;
785
    fmt_mvprintw(
786
        0i32,
787
        0i32,
788
        format_args!("robotfindskitten v{:}\n\n", { ver }),
789
        S_,
790
    );
791
    counter = 0i32;
792
    while counter <= (*S_).win.as_ref().unwrap().get_max_x() - 1i32 {
793
        fmt_printw(format_args!("{:}", 95i32 as u8 as char), S_);
794
        counter += 1
795
    }
796
    counter = 0i32;
797
    while counter < (*S_).num_bogus {
798
        draw((*S_).bogus[counter as usize], S_);
799
        counter += 1
800
    }
801
    draw((*S_).kitten, S_);
802
    draw((*S_).robot, S_);
803
    (*S_).win.as_ref().unwrap().refresh();
804
}
805
#[no_mangle]
806
extern "C" fn draw(mut o: screen_object, S_: &mut State) {
807
    full_draw(o, 0 != 0i32, S_);
808
}
809
#[no_mangle]
810
extern "C" fn full_draw(mut o: screen_object, mut in_place: bool, S_: &mut State) {
811
    let mut old: attr_t = 0;
812
    let mut dummy: libc::c_short = 0;
813
    let mut new: attr_t = 0;
814
    if !(stdscr as *const libc::c_void).is_null() {
815
        if !(&mut old as *mut attr_t as *const libc::c_void).is_null() {
816
            old = (*stdscr)._attrs
817
        } else {
818
        };
819
        if !(&mut dummy as *mut libc::c_short as *const libc::c_void).is_null() {
820
            dummy = (((*stdscr)._attrs & (1u64 << 8i32).wrapping_sub(1u64) << 0i32 + 8i32) >> 8i32)
821
                as libc::c_int as libc::c_short
822
        } else {
823
        };
824
    } else {
825
    };
826
    new = (o.color as chtype) << 0i32 + 8i32 & (1u64 << 8i32).wrapping_sub(1u64) << 0i32 + 8i32;
827
    if o.character as libc::c_int == '#' as i32 {
828
        new |= 1u64 << 12i32 + 8i32
829
    }
830
    if o.character as libc::c_int <= '\u{1a}' as i32 {
831
        new |= 1u64 << 14i32 + 8i32
832
    }
833
    if o.bold {
834
        new |= 1u64 << 13i32 + 8i32
835
    }
836
    (*S_)
837
        .win
838
        .as_ref()
839
        .unwrap()
840
        .attrset(new as libc::c_int as ::pancurses::chtype);
841
    if in_place {
842
        fmt_printw(
843
            format_args!("{:}", o.character as libc::c_int as u8 as char),
844
            S_,
845
        );
846
    } else {
847
        fmt_mvprintw(
848
            o.y,
849
            o.x,
850
            format_args!("{:}", o.character as libc::c_int as u8 as char),
851
            S_,
852
        );
853
        (*S_).win.as_ref().unwrap().mv(o.y, o.x);
854
    }
855
    (*S_)
856
        .win
857
        .as_ref()
858
        .unwrap()
859
        .attrset(old as libc::c_int as ::pancurses::chtype);
860
}
861
#[no_mangle]
862
extern "C" fn instructions(S_: &mut State) {
863
    let mut dummy: libc::c_char = 0;
864
    fmt_mvprintw(
865
        0i32,
866
        0i32,
867
        format_args!("robotfindskitten v{:}\n", { ver }),
868
        S_,
869
    );
870
    fmt_printw(
871
        format_args!("By the illustrious Leonard Richardson (C) 1997, 2000\n"),
872
        S_,
873
    );
874
    fmt_printw(
875
        format_args!("Written originally for the Nerth Pork robotfindskitten contest\n\n"),
876
        S_,
877
    );
878
    fmt_printw(format_args!("In this game, you are robot ("), S_);
879
    draw_in_place((*S_).robot, S_);
880
    fmt_printw(
881
        format_args!("). Your job is to find kitten. This task\n"),
882
        S_,
883
    );
884
    fmt_printw(
885
        format_args!("is complicated by the existence of various things which are not kitten.\n"),
886
        S_,
887
    );
888
    fmt_printw(
889
        format_args!("Robot must touch items to determine if they are kitten or not. The game\n"),
890
        S_,
891
    );
892
    fmt_printw(
893
        format_args!(
894
            "ends when robotfindskitten. Alternatively, you may end the game by hitting\n"
895
        ),
896
        S_,
897
    );
898
    fmt_printw(
899
        format_args!("the Esc key. See the documentation for more information.\n\n"),
900
        S_,
901
    );
902
    fmt_printw(format_args!("Press any key to start.\n"), S_);
903
    (*S_).win.as_ref().unwrap().refresh();
904
    dummy = ::encode_input((*S_).win.as_ref().unwrap().getch()) as libc::c_char;
905
    (*S_).win.as_ref().unwrap().clear();
906
}
907
#[no_mangle]
908
extern "C" fn draw_in_place(mut o: screen_object, S_: &mut State) {
909
    full_draw(o, 0 != 1i32, S_);
910
}
911
/*Game functions*/
912
#[no_mangle]
913
extern "C" fn play_game(S_: &mut State) {
914
    let mut old_x: libc::c_int = (*S_).robot.x;
915
    let mut old_y: libc::c_int = (*S_).robot.y;
916
    let mut input: libc::c_int = 0;
917
    input = ::encode_input((*S_).win.as_ref().unwrap().getch());
918
    while input != 27i32 && input != 'q' as i32 && input != 'Q' as i32 {
919
        process_input(input, S_);
920
        if !(old_x == (*S_).robot.x && old_y == (*S_).robot.y) {
921
            if (*S_).win.as_ref().unwrap().mv(old_y, old_x) == -1i32 {
922
            } else {
923
                (*S_).win.as_ref().unwrap().addch(' ' as i32 as chtype);
924
            };
925
            (*S_).screen[old_x as isize as usize][old_y as isize as usize] = -1i32;
926
            draw((*S_).robot, S_);
927
            (*S_).win.as_ref().unwrap().refresh();
928
            (*S_).screen[(*S_).robot.x as isize as usize][(*S_).robot.y as isize as usize] = 0i32;
929
            old_x = (*S_).robot.x;
930
            old_y = (*S_).robot.y
931
        }
932
        input = ::encode_input((*S_).win.as_ref().unwrap().getch())
933
    }
934
    message("Bye!", S_);
935
    (*S_).win.as_ref().unwrap().refresh();
936
    finish(0i32);
937
}
938
#[no_mangle]
939
extern "C" fn message(mut message_0: &str, S_: &mut State) {
940
    (*S_).win.as_ref().unwrap().mv(1i32, 0i32);
941
    (*S_).win.as_ref().unwrap().clrtoeol();
942
    fmt_mvprintw(
943
        1i32,
944
        0i32,
945
        format_args!("{:.*}", (*S_).win.as_ref().unwrap().get_max_x() as usize, {
946
            &message_0.to_owned()
947
        }),
948
        S_,
949
    );
950
    (*S_).win.as_ref().unwrap().mv((*S_).robot.y, (*S_).robot.x);
951
    (*S_).win.as_ref().unwrap().refresh();
952
}
953
#[no_mangle]
954
extern "C" fn process_input(mut input: libc::c_int, S_: &mut State) {
955
    let mut check_x: libc::c_int = (*S_).robot.x;
956
    let mut check_y: libc::c_int = (*S_).robot.y;
957
    match input {
958
        12 => {
959
            (*S_).win.as_ref().unwrap().refresh();
960
        }
961
        259 | 107 | 75 | 16 => check_y -= 1,
962
        262 | 121 | 89 => {
963
            check_x -= 1;
964
            check_y -= 1
965
        }
966
        339 | 117 | 85 => {
967
            check_x += 1;
968
            check_y -= 1
969
        }
970
        258 | 106 | 74 | 14 => check_y += 1,
971
        360 | 98 | 66 => {
972
            check_x -= 1;
973
            check_y += 1
974
        }
975
        338 | 110 | 78 => {
976
            check_x += 1;
977
            check_y += 1
978
        }
979
        260 | 104 | 72 | 2 => check_x -= 1,
980
        261 | 108 | 76 | 6 => check_x += 1,
981
        0 => {}
982
        _ => {
983
            message("Invalid input: Use direction keys or Esc.", S_);
984
            return;
985
        }
986
    }
987
    if check_y < 3i32
988
        || check_y > (*S_).win.as_ref().unwrap().get_max_y() - 1i32
989
        || check_x < 0i32
990
        || check_x > (*S_).win.as_ref().unwrap().get_max_x() - 1i32
991
    {
992
        return;
993
    }
994
    if (*S_).screen[check_x as isize as usize][check_y as isize as usize] != -1i32 {
995
        match (*S_).screen[check_x as isize as usize][check_y as isize as usize] {
996
            0 => {}
997
            1 => {
998
                /*We didn't move, or we're stuck in a
999
                time warp or something.*/
1000
                (*S_).win.as_ref().unwrap().mv(1i32, 0i32);
1001
                (*S_).win.as_ref().unwrap().clrtoeol();
1002
                play_animation(input, S_);
1003
            }
1004
            _ => {
1005
                message(
1006
                    messages[(*S_).bogus_messages[((*S_).screen[check_x as isize as usize]
1007
                        [check_y as isize as usize]
1008
                        - 2i32) as usize] as usize],
1009
                    S_,
1010
                );
1011
            }
1012
        }
1013
        return;
1014
    }
1015
    (*S_).robot.x = check_x;
1016
    (*S_).robot.y = check_y;
1017
}
1018
#[no_mangle]
1019
extern "C" fn play_animation(mut input: libc::c_int, S_: &mut State) {
1020
    let mut counter: libc::c_int = 0;
1021
    counter = 4i32;
1022
    while counter > 0i32 {
1023
        if (*S_).win.as_ref().unwrap().mv(1i32, 50i32 + counter + 1i32) == -1i32 {
1024
        } else {
1025
            (*S_).win.as_ref().unwrap().addch(' ' as i32 as chtype);
1026
        };
1027
        (*S_).win.as_ref().unwrap().mv(1i32, 50i32 + counter);
1028
        if input == 0o405i32 || input == 0o402i32 || input == 0o540i32 || input == 0o535i32 {
1029
            draw_in_place((*S_).kitten, S_);
1030
        } else {
1031
            draw_in_place((*S_).robot, S_);
1032
        }
1033
        if (*S_).win.as_ref().unwrap().mv(1i32, 50i32 - counter) == -1i32 {
1034
        } else {
1035
            (*S_).win.as_ref().unwrap().addch(' ' as i32 as chtype);
1036
        };
1037
        (*S_).win.as_ref().unwrap().mv(1i32, 50i32 - counter + 1i32);
1038
        if input == 0o405i32 || input == 0o402i32 || input == 0o540i32 || input == 0o535i32 {
1039
            draw_in_place((*S_).robot, S_);
1040
        } else {
1041
            draw_in_place((*S_).kitten, S_);
1042
        }
1043
        (*S_).win.as_ref().unwrap().refresh();
1044
        ::std::thread::sleep(::std::time::Duration::from_secs(
1045
            1i32 as libc::c_uint as u64,
1046
        ));
1047
        counter -= 1
1048
    }
1049
    (*S_).win.as_ref().unwrap().mv(1i32, 0i32);
1050
    (*S_)
1051
        .win
1052
        .as_ref()
1053
        .unwrap()
1054
        .addnstr("You found kitten! Way to go, robot!", -1i32 as usize);
1055
    (*S_).win.as_ref().unwrap().refresh();
1056
    finish(0i32);
1057
}
1058
fn main_0(
1059
    mut argc: libc::c_int,
1060
    mut argv: ::c2rust_runtime::CArray<Option<&::std::ffi::CStr>>,
1061
) -> libc::c_int {
1062
    let mut S: State = State {
1063
        win: None,
1064
        bogus: [screen_object {
1065
            x: 0,
1066
            y: 0,
1067
            color: 0,
1068
            bold: false,
1069
            character: 0,
1070
        }; 406],
1071
        bogus_messages: [0; 406],
1072
        used_messages: [0; 406],
1073
        screen: { ::c2rust_runtime::CArray::empty() },
1074
        robot: screen_object {
1075
            x: 0,
1076
            y: 0,
1077
            color: 0,
1078
            bold: false,
1079
            character: 0,
1080
        },
1081
        kitten: screen_object {
1082
            x: 0,
1083
            y: 0,
1084
            color: 0,
1085
            bold: false,
1086
            character: 0,
1087
        },
1088
        num_bogus: 0,
1089
    };
1090
    if argc == 1i32 {
1091
        S.num_bogus = 20i32
1092
    } else {
1093
        S.num_bogus = as ::std::str::FromStr>::from_str(
1094
            (**argv.offset(1isize)).unwrap().to_str().unwrap(),
1095
        )
1096
        .unwrap();
1097
        if S.num_bogus < 0i32
1098
            || S.num_bogus as libc::c_ulong
1099
                > (::std::mem::size_of::<[*mut libc::c_char; 406]>() as libc::c_ulong)
1100
                    .wrapping_div(::std::mem::size_of::<*mut libc::c_char>() as libc::c_ulong)
1101
        {
1102
            fmt_printf(format_args!(
1103
                "Run-time parameter must be between 0 and {:}.\n",
1104
                (::std::mem::size_of::<[*mut libc::c_char; 406]>() as libc::c_ulong)
1105
                    .wrapping_div(::std::mem::size_of::<*mut libc::c_char>() as libc::c_ulong)
1106
                    as libc::c_int
1107
            ));
1108
            ::std::process::exit(0i32 as i32);
1109
        }
1110
    }
1111
    ();
1112
    fmt_printf(format_args!(
1113
        "{:}{:}{:}",
1114
        27i32 as u8 as char, '(' as i32 as u8 as char, 'U' as i32 as u8 as char
1115
    ));
1116
    initialize_ncurses(&mut S);
1117
    initialize_arrays(&mut S);
1118
    initialize_robot(&mut S);
1119
    initialize_kitten(&mut S);
1120
    initialize_bogus(&mut S);
1121
    instructions(&mut S);
1122
    initialize_screen(&mut S);
1123
    play_game(&mut S);
1124
    return 0;
1125
}
1126
fn main() {
1127
    // Collect argv into a vector.
1128
    let mut args_owned: Vec<::std::ffi::CString> = Vec::new();
1129
    for arg in ::std::env::args() {
1130
        args_owned.push(::std::ffi::CString::new(arg).unwrap());
1131
    }
1132
1133
    // Now that the length is known, we can build a CArray.
1134
    let mut args: ::c2rust_runtime::CArray<Option<&::std::ffi::CStr>> =
1135
        ::c2rust_runtime::CArray::alloc(args_owned.len() + 1);
1136
    for i in 0..args_owned.len() {
1137
        args[i] = Some(&args_owned[i]);
1138
    }
1139
    // The last element of `args` remains `None`.
1140
1141
    {
1142
        ::std::process::exit(main_0((args.len() - 1) as libc::c_int, args) as i32);
1143
    }
1144
}