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.*/