[PATCH 0/1] MR7717: Initial patch for converting Wine to Rust
Now let AJ figure out the build support and we can convert the rest -- https://gitlab.winehq.org/wine/wine/-/merge_requests/7717
From: André Zwing <nerv(a)dawncrow.de> --- tools/wine/Makefile.in | 2 +- tools/wine/wine.c | 67 -------------- tools/wine/wine.rs | 194 +++++++++++++++++++++++++++++++++++++++++ 3 files changed, 195 insertions(+), 68 deletions(-) delete mode 100644 tools/wine/wine.c create mode 100644 tools/wine/wine.rs diff --git a/tools/wine/Makefile.in b/tools/wine/Makefile.in index 55489a3444a..7c668cabc41 100644 --- a/tools/wine/Makefile.in +++ b/tools/wine/Makefile.in @@ -1,7 +1,7 @@ PROGRAMS = wine SOURCES = \ - wine.c \ + wine.rs \ wine.de.UTF-8.man.in \ wine.fr.UTF-8.man.in \ wine.man.in \ diff --git a/tools/wine/wine.c b/tools/wine/wine.c deleted file mode 100644 index 32157dfd4bc..00000000000 --- a/tools/wine/wine.c +++ /dev/null @@ -1,67 +0,0 @@ -/* - * Loader for Wine installed in the bin directory - * - * Copyright 2025 Alexandre Julliard - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2.1 of the License, or (at your option) any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the Free Software - * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA - */ - -#include "config.h" - -#include "../tools.h" - -#include <dlfcn.h> - -static const char *bindir; -static const char *libdir; - -static void *load_ntdll(void) -{ - const char *arch_dir = get_arch_dir( get_default_target() ); - struct strarray dllpath; - void *handle; - unsigned int i; - - if (bindir && strendswith( bindir, "/tools/wine" ) && - ((handle = dlopen( strmake( "%s/../../dlls/ntdll/ntdll.so", bindir ), RTLD_NOW )))) - return handle; - - if ((handle = dlopen( strmake( "%s/wine%s/ntdll.so", libdir, arch_dir ), RTLD_NOW ))) - return handle; - - dllpath = strarray_frompath( getenv( "WINEDLLPATH" )); - for (i = 0; i < dllpath.count; i++) - { - if ((handle = dlopen( strmake( "%s%s/ntdll.so", dllpath.str[i], arch_dir ), RTLD_NOW ))) - return handle; - if ((handle = dlopen( strmake( "%s/ntdll.so", dllpath.str[i] ), RTLD_NOW ))) - return handle; - } - fprintf( stderr, "wine: could not load ntdll.so: %s\n", dlerror() ); - exit(1); -} - -int main( int argc, char *argv[] ) -{ - void (*init_func)(int, char **); - - bindir = get_bindir( argv[0] ); - libdir = get_libdir( bindir ); - init_func = dlsym( load_ntdll(), "__wine_main" ); - if (init_func) init_func( argc, argv ); - - fprintf( stderr, "wine: __wine_main function not found in ntdll.so\n" ); - exit(1); -} diff --git a/tools/wine/wine.rs b/tools/wine/wine.rs new file mode 100644 index 00000000000..17eb99586e4 --- /dev/null +++ b/tools/wine/wine.rs @@ -0,0 +1,194 @@ +/* + * Loader for Wine installed in the bin directory + * + * Copyright 2025 Alexandre Julliard + * Copyright 2025 André Zwing + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA + */ + +use libloading::{Library, Symbol}; +use std::{env, ffi::CString, fs::canonicalize, process::exit}; + +#[cfg(target_os = "linux")] +fn get_bindir(argv0: &str) -> Option<String> { + if let Ok(real_path) = canonicalize("/proc/self/exe") { + return Some(real_path.parent()?.to_str()?.to_string()); + } + canonicalize(argv0) + .ok()? + .parent()? + .to_str() + .map(|s| s.to_string()) +} + +#[cfg(target_os = "windows")] +fn get_bindir(_argv0: &str) -> Option<String> { + use std::ptr; + use winapi::um::{libloaderapi::GetModuleFileNameA, winnt::MAX_PATH}; + + let mut path = vec![0u8; MAX_PATH as usize]; + unsafe { + GetModuleFileNameA(ptr::null_mut(), path.as_mut_ptr() as *mut i8, MAX_PATH); + } + let path_str = String::from_utf8_lossy(&path); + Some( + PathBuf::from(path_str.trim_end_matches(char::from(0))) + .parent()? + .to_str()? + .to_string(), + ) +} + +fn get_libdir(bindir: &str) -> String { + format!("{}/../lib", bindir) +} + +#[allow(dead_code)] +#[derive(Debug)] +struct Target { + cpu: &'static str, + platform: &'static str, +} + +fn get_default_target() -> Target { + let cpu = if cfg!(target_arch = "x86") { + "i386" + } else if cfg!(target_arch = "x86_64") { + "x86_64" + } else if cfg!(target_arch = "arm") { + "arm" + } else if cfg!(target_arch = "aarch64") { + "aarch64" + } else { + panic!("Unsupported CPU"); + }; + + let platform = if cfg!(target_os = "macos") { + "apple" + } else if cfg!(target_os = "android") { + "android" + } else if cfg!(target_os = "linux") { + "linux" + } else if cfg!(target_os = "freebsd") { + "freebsd" + } else if cfg!(target_os = "solaris") { + "solaris" + } else if cfg!(target_os = "windows") { + "mingw" + } else { + "unspecified" + }; + + Target { cpu, platform } +} + +fn get_arch_dir(target: &Target) -> String { + let cpu_names = [ + ("i386", "i386"), + ("x86_64", "x86_64"), + ("arm", "arm"), + ("aarch64", "aarch64"), + ]; + + let cpu_name = cpu_names + .iter() + .find(|&&(key, _)| key == target.cpu) + .map(|&(_, name)| name); + if let Some(name) = cpu_name { + format!( + "/{}/{}", + name, + if is_pe_target(target) { + "windows" + } else { + "unix" + } + ) + } else { + "".to_string() + } +} + +fn is_pe_target(_target: &Target) -> bool { + // Implement platform-specific PE detection logic if needed + false +} + +fn load_ntdll(bindir_in: Option<String>, libdir_in: Option<String>) -> Library { + let arch_dir = get_arch_dir(&get_default_target()); + let winedllpath = env::var("WINEDLLPATH").unwrap_or_default(); + + unsafe { + if let Some(ref bindir) = bindir_in { + if bindir.ends_with("/tools/wine") { + let path = format!("{}/../../dlls/ntdll/ntdll.so", bindir); + if let Ok(lib) = Library::new(&path) { + return lib; + } + } + } + + if let Some(ref libdir) = libdir_in { + let path = format!("{}/wine{}/ntdll.so", libdir, arch_dir); + if let Ok(lib) = Library::new(&path) { + return lib; + } + } + + for path in winedllpath.split(':') { + let full_path1 = format!("{}{}{}/ntdll.so", path, arch_dir, ""); + if let Ok(lib) = Library::new(&full_path1) { + return lib; + } + let full_path2 = format!("{}/ntdll.so", path); + if let Ok(lib) = Library::new(&full_path2) { + return lib; + } + } + } + + eprintln!("wine: could not load ntdll.so"); + exit(1); +} + +fn main() { + let args: Vec<String> = env::args().collect(); + + let bindir: Option<String> = get_bindir(&args[0]); + let libdir: Option<String> = Some(get_libdir(bindir.as_ref().unwrap())); + + let ntdll = load_ntdll(bindir, libdir); + + unsafe { + let init_func: Result<Symbol<unsafe extern "C" fn(i32, *mut *mut i8)>, _> = + ntdll.get(b"__wine_main"); + + match init_func { + Ok(func) => { + let mut c_args: Vec<*mut i8> = args + .iter() + .map(|arg| CString::new(arg.as_str()).unwrap().into_raw()) + .collect(); + + func(args.len() as i32, c_args.as_mut_ptr()); + } + Err(_) => { + eprintln!("wine: __wine_main function not found in ntdll.so"); + exit(1); + } + } + } +} -- GitLab https://gitlab.winehq.org/wine/wine/-/merge_requests/7717
This merge request was approved by Alfred Agrell. -- https://gitlab.winehq.org/wine/wine/-/merge_requests/7717
Isn't Rust's support for implementing variadic functions still incomplete? Other than that, LGTM. We can deal with that when we get there. -- https://gitlab.winehq.org/wine/wine/-/merge_requests/7717#note_99528
On Tue Apr 1 16:52:54 2025 +0000, Alfred Agrell wrote:
Isn't Rust's support for implementing variadic functions still incomplete? Other than that, LGTM. We can deal with that when we get there. thanks! variadic interfaces for Rust libraries is an unstable feature, so you have to use nightly
-- https://gitlab.winehq.org/wine/wine/-/merge_requests/7717#note_99530
Now let AJ figure out the build support and we can convert the rest
Sure, I'll get right on to it, this is clearly top priority. -- https://gitlab.winehq.org/wine/wine/-/merge_requests/7717#note_99546
This patch uses the `unsafe` keyword four times. Can't we just wrap the entire file in one `unsafe` block and call it a day? -- https://gitlab.winehq.org/wine/wine/-/merge_requests/7717#note_99548
This merge request was approved by Etaash Mathamsetty. -- https://gitlab.winehq.org/wine/wine/-/merge_requests/7717
This is missing tests, this should first add support for `todo_rust` which allows failures if the target dll/exe was not written in Rust, add `todo_rust` tests for `wine.c`, and then introduce `wine.rs`. -- https://gitlab.winehq.org/wine/wine/-/merge_requests/7717#note_99577
Jinoh Kang (@iamahuman) commented about tools/wine/wine.rs:
+struct Target { + cpu: &'static str, + platform: &'static str, +} + +fn get_default_target() -> Target { + let cpu = if cfg!(target_arch = "x86") { + "i386" + } else if cfg!(target_arch = "x86_64") { + "x86_64" + } else if cfg!(target_arch = "arm") { + "arm" + } else if cfg!(target_arch = "aarch64") { + "aarch64" + } else { + panic!("Unsupported CPU"); What about arm64ec?
-- https://gitlab.winehq.org/wine/wine/-/merge_requests/7717#note_99608
Jinoh Kang (@iamahuman) commented about tools/wine/wine.rs:
+ * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA + */ + +use libloading::{Library, Symbol}; You need `extern crate libloading` for this.
-- https://gitlab.winehq.org/wine/wine/-/merge_requests/7717#note_99604
Jinoh Kang (@iamahuman) commented about tools/wine/wine.rs:
+use libloading::{Library, Symbol}; +use std::{env, ffi::CString, fs::canonicalize, process::exit}; + +#[cfg(target_os = "linux")] +fn get_bindir(argv0: &str) -> Option<String> { + if let Ok(real_path) = canonicalize("/proc/self/exe") { + return Some(real_path.parent()?.to_str()?.to_string()); + } + canonicalize(argv0) + .ok()? + .parent()? + .to_str() + .map(|s| s.to_string()) +} + +#[cfg(target_os = "windows")] I see that you're finally taking up the long overdue work to make Wine run on...Windows.
A giant step to the sommelierkind. That said, split the windows support into its own commit. -- https://gitlab.winehq.org/wine/wine/-/merge_requests/7717#note_99605
Jinoh Kang (@iamahuman) commented about tools/wine/wine.rs:
+ return Some(real_path.parent()?.to_str()?.to_string()); + } + canonicalize(argv0) + .ok()? + .parent()? + .to_str() + .map(|s| s.to_string()) +} + +#[cfg(target_os = "windows")] +fn get_bindir(_argv0: &str) -> Option<String> { + use std::ptr; + use winapi::um::{libloaderapi::GetModuleFileNameA, winnt::MAX_PATH}; + + let mut path = vec![0u8; MAX_PATH as usize]; + unsafe {
// SAFETY: As certified and decreed by the Sommelier Oxidization Council
unsafe {
-- https://gitlab.winehq.org/wine/wine/-/merge_requests/7717#note_99606
Jinoh Kang (@iamahuman) commented about tools/wine/wine.rs:
+ .ok()? + .parent()? + .to_str() + .map(|s| s.to_string()) +} + +#[cfg(target_os = "windows")] +fn get_bindir(_argv0: &str) -> Option<String> { + use std::ptr; + use winapi::um::{libloaderapi::GetModuleFileNameA, winnt::MAX_PATH}; + + let mut path = vec![0u8; MAX_PATH as usize]; + unsafe { + GetModuleFileNameA(ptr::null_mut(), path.as_mut_ptr() as *mut i8, MAX_PATH); + } + let path_str = String::from_utf8_lossy(&path); This causes regressions in non-UTF8 Unix path which would previously be handled without problem. Please use OsString instead.
The regression occurs despite a very minor fact that it has never been possible to run the loader on Windows before. -- https://gitlab.winehq.org/wine/wine/-/merge_requests/7717#note_99607
Jinoh Kang (@iamahuman) commented about tools/wine/wine.rs:
+ }; + + let platform = if cfg!(target_os = "macos") { + "apple" + } else if cfg!(target_os = "android") { + "android" + } else if cfg!(target_os = "linux") { + "linux" + } else if cfg!(target_os = "freebsd") { + "freebsd" + } else if cfg!(target_os = "solaris") { + "solaris" + } else if cfg!(target_os = "windows") { + "mingw" + } else { + "unspecified" Missing netbsd.
Also, where is Windows Phone and POSReady? An oversight? -- https://gitlab.winehq.org/wine/wine/-/merge_requests/7717#note_99610
Jinoh Kang (@iamahuman) commented about tools/wine/wine.rs:
+ Target { cpu, platform } +} + +fn get_arch_dir(target: &Target) -> String { + let cpu_names = [ + ("i386", "i386"), + ("x86_64", "x86_64"), + ("arm", "arm"), + ("aarch64", "aarch64"), + ]; + + let cpu_name = cpu_names + .iter() + .find(|&&(key, _)| key == target.cpu) + .map(|&(_, name)| name); + if let Some(name) = cpu_name { You can simply panic here. Why complicate matters?
-- https://gitlab.winehq.org/wine/wine/-/merge_requests/7717#note_99609
Jinoh Kang (@iamahuman) commented about tools/wine/wine.rs:
+ if bindir.ends_with("/tools/wine") { + let path = format!("{}/../../dlls/ntdll/ntdll.so", bindir); + if let Ok(lib) = Library::new(&path) { + return lib; + } + } + } + + if let Some(ref libdir) = libdir_in { + let path = format!("{}/wine{}/ntdll.so", libdir, arch_dir); + if let Ok(lib) = Library::new(&path) { + return lib; + } + } + + for path in winedllpath.split(':') { Please use `;` if `cfg!(target_os = "windows")`.
-- https://gitlab.winehq.org/wine/wine/-/merge_requests/7717#note_99616
Jinoh Kang (@iamahuman) commented about tools/wine/wine.rs:
+ "/{}/{}", + name, + if is_pe_target(target) { + "windows" + } else { + "unix" + } + ) + } else { + "".to_string() + } +} + +fn is_pe_target(_target: &Target) -> bool { + // Implement platform-specific PE detection logic if needed + false
// What could go wrong?
cfg!(target_os = "windows")
-- https://gitlab.winehq.org/wine/wine/-/merge_requests/7717#note_99611
Jinoh Kang (@iamahuman) commented about tools/wine/wine.rs:
+ "unix" + } + ) + } else { + "".to_string() + } +} + +fn is_pe_target(_target: &Target) -> bool { + // Implement platform-specific PE detection logic if needed + false +} + +fn load_ntdll(bindir_in: Option<String>, libdir_in: Option<String>) -> Library { + let arch_dir = get_arch_dir(&get_default_target()); + let winedllpath = env::var("WINEDLLPATH").unwrap_or_default(); Is using Option an option?
-- https://gitlab.winehq.org/wine/wine/-/merge_requests/7717#note_99612
Jinoh Kang (@iamahuman) commented about tools/wine/wine.rs:
+ ) + } else { + "".to_string() + } +} + +fn is_pe_target(_target: &Target) -> bool { + // Implement platform-specific PE detection logic if needed + false +} + +fn load_ntdll(bindir_in: Option<String>, libdir_in: Option<String>) -> Library { + let arch_dir = get_arch_dir(&get_default_target()); + let winedllpath = env::var("WINEDLLPATH").unwrap_or_default(); + + unsafe {
// SAFETY: As certified and decreed by the Sommelier Oxidization Council
unsafe {
-- https://gitlab.winehq.org/wine/wine/-/merge_requests/7717#note_99613
Jinoh Kang (@iamahuman) commented about tools/wine/wine.rs:
+ } else { + "".to_string() + } +} + +fn is_pe_target(_target: &Target) -> bool { + // Implement platform-specific PE detection logic if needed + false +} + +fn load_ntdll(bindir_in: Option<String>, libdir_in: Option<String>) -> Library { + let arch_dir = get_arch_dir(&get_default_target()); + let winedllpath = env::var("WINEDLLPATH").unwrap_or_default(); + + unsafe { + if let Some(ref bindir) = bindir_in { Prefer ergonomic patterns whenever possible.
Let's forget the bad ol' days of pointers[^1]. [^1]: Let's also forget the fact that Rust actually, still, has pointers. -- https://gitlab.winehq.org/wine/wine/-/merge_requests/7717#note_99614
Jinoh Kang (@iamahuman) commented about tools/wine/wine.rs:
+ +fn load_ntdll(bindir_in: Option<String>, libdir_in: Option<String>) -> Library { + let arch_dir = get_arch_dir(&get_default_target()); + let winedllpath = env::var("WINEDLLPATH").unwrap_or_default(); + + unsafe { + if let Some(ref bindir) = bindir_in { + if bindir.ends_with("/tools/wine") { + let path = format!("{}/../../dlls/ntdll/ntdll.so", bindir); + if let Ok(lib) = Library::new(&path) { + return lib; + } + } + } + + if let Some(ref libdir) = libdir_in { Ditto.
-- https://gitlab.winehq.org/wine/wine/-/merge_requests/7717#note_99615
Jinoh Kang (@iamahuman) commented about tools/wine/wine.rs:
+ } + } + + for path in winedllpath.split(':') { + let full_path1 = format!("{}{}{}/ntdll.so", path, arch_dir, ""); + if let Ok(lib) = Library::new(&full_path1) { + return lib; + } + let full_path2 = format!("{}/ntdll.so", path); + if let Ok(lib) = Library::new(&full_path2) { + return lib; + } + } + } + + eprintln!("wine: could not load ntdll.so");
eprintln!("wine-rs: could not load ntdll.so");
To signify the giant step forward -- https://gitlab.winehq.org/wine/wine/-/merge_requests/7717#note_99617
Jinoh Kang (@iamahuman) commented about tools/wine/wine.rs:
+} + +fn is_pe_target(_target: &Target) -> bool { + // Implement platform-specific PE detection logic if needed + false +} + +fn load_ntdll(bindir_in: Option<String>, libdir_in: Option<String>) -> Library { + let arch_dir = get_arch_dir(&get_default_target()); + let winedllpath = env::var("WINEDLLPATH").unwrap_or_default(); + + unsafe { + if let Some(ref bindir) = bindir_in { + if bindir.ends_with("/tools/wine") { + let path = format!("{}/../../dlls/ntdll/ntdll.so", bindir); + if let Ok(lib) = Library::new(&path) { You could make `path` a temporary here. It's being unnecessary verbose.
-- https://gitlab.winehq.org/wine/wine/-/merge_requests/7717#note_99618
Jinoh Kang (@iamahuman) commented about tools/wine/wine.rs:
+ } + } + + eprintln!("wine: could not load ntdll.so"); + exit(1); +} + +fn main() { + let args: Vec<String> = env::args().collect(); + + let bindir: Option<String> = get_bindir(&args[0]); + let libdir: Option<String> = Some(get_libdir(bindir.as_ref().unwrap())); + + let ntdll = load_ntdll(bindir, libdir); + + unsafe {
// SAFETY: As certified and decreed by the Sommelier Oxidization Council
unsafe {
-- https://gitlab.winehq.org/wine/wine/-/merge_requests/7717#note_99619
Jinoh Kang (@iamahuman) commented about tools/wine/wine.rs:
+ } + + eprintln!("wine: could not load ntdll.so"); + exit(1); +} + +fn main() { + let args: Vec<String> = env::args().collect(); + + let bindir: Option<String> = get_bindir(&args[0]); + let libdir: Option<String> = Some(get_libdir(bindir.as_ref().unwrap())); + + let ntdll = load_ntdll(bindir, libdir); + + unsafe { + let init_func: Result<Symbol<unsafe extern "C" fn(i32, *mut *mut i8)>, _> = Can we simplify the Result with let-else?
Or else. -- https://gitlab.winehq.org/wine/wine/-/merge_requests/7717#note_99620
Jinoh Kang (@iamahuman) commented about tools/wine/wine.rs:
+ let args: Vec<String> = env::args().collect(); + + let bindir: Option<String> = get_bindir(&args[0]); + let libdir: Option<String> = Some(get_libdir(bindir.as_ref().unwrap())); + + let ntdll = load_ntdll(bindir, libdir); + + unsafe { + let init_func: Result<Symbol<unsafe extern "C" fn(i32, *mut *mut i8)>, _> = + ntdll.get(b"__wine_main"); + + match init_func { + Ok(func) => { + let mut c_args: Vec<*mut i8> = args + .iter() + .map(|arg| CString::new(arg.as_str()).unwrap().into_raw()) What's the point of the Rust when we're doing the C?
-- https://gitlab.winehq.org/wine/wine/-/merge_requests/7717#note_99621
Jinoh Kang (@iamahuman) commented about tools/wine/wine.rs:
+ + unsafe { + let init_func: Result<Symbol<unsafe extern "C" fn(i32, *mut *mut i8)>, _> = + ntdll.get(b"__wine_main"); + + match init_func { + Ok(func) => { + let mut c_args: Vec<*mut i8> = args + .iter() + .map(|arg| CString::new(arg.as_str()).unwrap().into_raw()) + .collect(); + + func(args.len() as i32, c_args.as_mut_ptr()); + } + Err(_) => { + eprintln!("wine: __wine_main function not found in ntdll.so");
eprintln!("wine-rs: __wine_main function not found in ntdll.so");
Need I say more? This is a great omission that should have been caught before upstreaming -- https://gitlab.winehq.org/wine/wine/-/merge_requests/7717#note_99622
As for the build system, I propose replacing makedep with a build.rs script. -- https://gitlab.winehq.org/wine/wine/-/merge_requests/7717#note_99623
This merge request was closed by André Zwing. -- https://gitlab.winehq.org/wine/wine/-/merge_requests/7717
April Fools' Day is over, thanks for the reactions :smile: -- https://gitlab.winehq.org/wine/wine/-/merge_requests/7717#note_99723
participants (7)
-
Alex Henrie (@alexhenrie) -
Alexandre Julliard (@julliard) -
Alfred Agrell (@Alcaro) -
André Zwing -
Etaash Mathamsetty (@etaash.mathamsetty) -
Jinoh Kang (@iamahuman) -
Vibhav Pant (@vibhavp)