xprop setting multiple fields of atom property - linux

I searched internet and found examples with setting only one field of property:
xprop -id "$windowid" -f _NET_WM_STATE 32a -set _NET_WM_STATE _NET_WM_STATE_ABOVE
but how can I set multiple fields?
I tried:
xprop -id "$windowid" -f _NET_WM_STATE 32a -set _NET_WM_STATE '_NET_WM_STATE_ABOVE, _NET_WM_STATE_SKIP_TASKBAR'
and
xprop -id "$windowid" -f _NET_WM_STATE 32aa -set _NET_WM_STATE _NET_WM_STATE_ABOVE,_NET_WM_STATE_SKIP_TASKBAR
and many other variants with no luck.
Is it possible ? :)
Ok....
I wrote a patch for xprop to fix this, and It works, but don't know is it correct.
Thanks to #MichałGórny.
(xprop.c,v 1.6)
--- xprop.c 2012-07-31 11:24:01.178117974 +0400
+++ xprop.mod 2012-07-31 11:23:19.434784430 +0400
## -1487,11 +1487,20 ##
break;
}
case 'a': {
- static Atom avalue;
- avalue = Parse_Atom(value, False);
- type = XA_ATOM;
- data = (unsigned char *) &avalue;
- nelements = 1;
+ static unsigned long data32[MAXELEMENTS];
+ char * value2 = strdup(value);
+ char * tmp = strtok(value2,",");
+ nelements = 0;
+ while( NULL != tmp ){
+ data32[nelements] = Parse_Atom(tmp, False);
+ nelements +=1;
+ if(nelements >= MAXELEMENTS)
+ break;
+ tmp = strtok(NULL,",");
+ }
+ type = XA_ATOM;
+ data = (unsigned char *) data32;
+ free(value2);
break;
}
case 'm':

Looking at the xprop's code, it's not possible.
case 'a': {
static Atom avalue;
avalue = Parse_Atom(value, False);
type = XA_ATOM;
data = (unsigned char *) &avalue;
nelements = 1;
break;
}
This is the code parsing the value to -set.
static Atom
Parse_Atom (const char *name, int only_if_exists)
{
/* may return None = 0 */
return XInternAtom(dpy, name, only_if_exists);
}
So it parses only a single atom.
I've also opened a bug for it; maybe they'll add this.

Related

How do I use escapeshellarg() on Windows but "aimed for Linux" (and vice versa)?

If PHP is running on Windows, escapeshellarg() escapes file names (for example) in a certain way and then adds " (DOUBLE) quotes around it.
If PHP is running on Linux, escapeshellarg() uses Linux-based escaping and then adds ' (SINGLE) quotes around it.
In my situation, I'm generating a SHA256SUMS file on Windows, but aimed for Linux. Since I use escapeshellarg() to escape the file name, I end up with a file like:
cabcdccas12exdqdqadanacvdkjsc123ccfcfq3rdwcndwf2qefcf "cool filename with spaces.zip"
However, Linux tools probably expect:
cabcdccas12exdqdqadanacvdkjsc123ccfcfq3rdwcndwf2qefcf 'cool filename with spaces.zip'
Looking in the manual, there seems to be no way to do something like: escapeshellarg($blabla, TARGET_OS_LINUX); in order for it to use the rules for Linux instead of the OS running the script (Windows).
I can't just str_replace the quotes because it would not take into consideration all the platform-specific rules.
Also, yes, I need spaces in the file name (and any other cross-platform-valid character).
I sadly found no mention whatsoever about the preferred quote style on the only source of information I have for this: https://help.ubuntu.com/community/HowToSHA256SUM
Maybe the SHA256 security verification tools which read that SHA256SUMS file understand and can parse both kinds?
The behavior of escapeshellarg() is hard-coded depending on whether PHP is running on Windows or any other operating system. You should reimplement escapeshellarg() for consistent behavior.
Here is my attempt at reimplementing escapeshellarg() with a Windows/other-OS toggle in PHP:
<?php namespace polyfill;
const TARGET_OS_WINDOWS = 1;
const TARGET_OS_UNIX = 2;
function escapeshellarg(string $input, int $os_mode = 0): string
{
if (false !== strpos($input, "\x00"))
{
throw new \UnexpectedValueException(__FUNCTION__ . '(): Argument #1 ($input) must not contain any null bytes');
}
if ($os_mode == 0)
{
$os_mode = TARGET_OS_UNIX;
if (strtoupper(substr(PHP_OS, 0, 3)) === 'WIN')
$os_mode = TARGET_OS_WINDOWS;
}
$maxlen = 4096;
if ($os_mode === TARGET_OS_WINDOWS) $maxlen = 8192;
if (strlen($input) > $maxlen - 2) return "";
if ($os_mode === TARGET_OS_WINDOWS)
{
$output =
str_replace(['"', '%', '!'],
[' ', ' ', ' '],
$input);
# https://bugs.php.net/bug.php?id=69646
if (substr($output, -1) === "\\")
{
$k = 0; $n = strlen($output) - 1;
for (; $n >= 0 && substr($output, $n, 1) === "\\"; $n--, $k++);
if ($k % 2) $output .= "\\";
}
$output = "\"$output\"";
}
else
{
$output = str_replace("'", "'\''", $input);
$output = "'$output'";
}
if (strlen($output) > $maxlen) return "";
return $output;
}
It should be almost functionally equivalent to the native PHP escapeshellarg(), except that:
it takes a second argument that sets whether you want the output in Windows mode or not Windows mode,
it raises an \UnexpectedValueException instead of some kind of PHP error if the input string contains null bytes,
it doesn't emit errors due to the input being too long, and
it has 4096 hard-coded as the maximum argument length on Unix-like platforms.
To use this replacement function:
# In Unix/Linux/macOS mode
\polyfill\escapeshellarg($blabla, \polyfill\TARGET_OS_UNIX);
# In Windows mode
\polyfill\escapeshellarg($blabla, \polyfill\TARGET_OS_WINDOWS);
# In auto-detect (running OS) mode
\polyfill\escapeshellarg($blabla);
Reference
Here is the full C implementation from PHP 7.3.10 (./ext/standard/exec.c):
PHPAPI zend_string *php_escape_shell_arg(char *str)
{
size_t x, y = 0;
size_t l = strlen(str);
zend_string *cmd;
uint64_t estimate = (4 * (uint64_t)l) + 3;
/* max command line length - two single quotes - \0 byte length */
if (l > cmd_max_len - 2 - 1) {
php_error_docref(NULL, E_ERROR, "Argument exceeds the allowed length of %zu bytes", cmd_max_len);
return ZSTR_EMPTY_ALLOC();
}
cmd = zend_string_safe_alloc(4, l, 2, 0); /* worst case */
#ifdef PHP_WIN32
ZSTR_VAL(cmd)[y++] = '"';
#else
ZSTR_VAL(cmd)[y++] = '\'';
#endif
for (x = 0; x < l; x++) {
int mb_len = php_mblen(str + x, (l - x));
/* skip non-valid multibyte characters */
if (mb_len < 0) {
continue;
} else if (mb_len > 1) {
memcpy(ZSTR_VAL(cmd) + y, str + x, mb_len);
y += mb_len;
x += mb_len - 1;
continue;
}
switch (str[x]) {
#ifdef PHP_WIN32
case '"':
case '%':
case '!':
ZSTR_VAL(cmd)[y++] = ' ';
break;
#else
case '\'':
ZSTR_VAL(cmd)[y++] = '\'';
ZSTR_VAL(cmd)[y++] = '\\';
ZSTR_VAL(cmd)[y++] = '\'';
#endif
/* fall-through */
default:
ZSTR_VAL(cmd)[y++] = str[x];
}
}
#ifdef PHP_WIN32
if (y > 0 && '\\' == ZSTR_VAL(cmd)[y - 1]) {
int k = 0, n = y - 1;
for (; n >= 0 && '\\' == ZSTR_VAL(cmd)[n]; n--, k++);
if (k % 2) {
ZSTR_VAL(cmd)[y++] = '\\';
}
}
ZSTR_VAL(cmd)[y++] = '"';
#else
ZSTR_VAL(cmd)[y++] = '\'';
#endif
ZSTR_VAL(cmd)[y] = '\0';
if (y > cmd_max_len + 1) {
php_error_docref(NULL, E_ERROR, "Escaped argument exceeds the allowed length of %zu bytes", cmd_max_len);
zend_string_release_ex(cmd, 0);
return ZSTR_EMPTY_ALLOC();
}
if ((estimate - y) > 4096) {
/* realloc if the estimate was way overill
* Arbitrary cutoff point of 4096 */
cmd = zend_string_truncate(cmd, y, 0);
}
ZSTR_LEN(cmd) = y;
return cmd;
}
// … [truncated] …
/* {{{ proto string escapeshellarg(string arg)
Quote and escape an argument for use in a shell command */
PHP_FUNCTION(escapeshellarg)
{
char *argument;
size_t argument_len;
ZEND_PARSE_PARAMETERS_START(1, 1)
Z_PARAM_STRING(argument, argument_len)
ZEND_PARSE_PARAMETERS_END();
if (argument) {
if (argument_len != strlen(argument)) {
php_error_docref(NULL, E_ERROR, "Input string contains NULL bytes");
return;
}
RETVAL_STR(php_escape_shell_arg(argument));
}
}
/* }}} */
The logic is fairly simple. Here are some equivalent functional test cases in prose:
The input string cannot contain NUL characters.
Applied to the input string,
in Windows mode,
Prepend a " character.
Replace all ", %, and ! characters with .
If the end consists of an odd number of \ characters, add one \ character to the end. (Bug #69646)
Append a " character.
in other platforms mode,
Prepend a ' character.
Replace all ' characters with '\''
Append a ' character.
On Windows, if the output is longer than 8192 characters, emit an E_ERROR and return an empty string.
On other platforms, if the output is longer than 4096 characters (or whatever the overridden maximum is at compile time), emit an E_ERROR and return an empty string.

CS50 - pset2 - Vigenere

I am currently struggling with pset2, specifically with vigenere.
Here is my code :
# include <cs50.h>
# include <ctype.h>
# include <stdio.h>
# include <stdlib.h>
# include <string.h>
int main (int argc, string argv[])
{
//Assess the fact that there is only 1 command line argument
if(argc != 2)
{
printf("You should only have 1 command line argument !\n") ;
return 1 ;
}
string k = argv[1] ;
int klength = strlen(k) ;
for(int i = 0; i < klength; i++)
{
if(!isalpha(k[i]))
{
printf("Please make sure the argument is only composed of alphabetical characters\n") ;
return 1 ;
}
}
//Get the text to be crypted
string s = GetString() ;
int slength = strlen(s) ;
//Deliver the crypted text
for( int i = 0, j = 0 ; i < slength ; i++)
{
int kindex = j % klength ;
if(isalpha(s[i]))
{
if(isupper(s[i]))
{
if(isupper(k[kindex]))
{
int crypt = (((s[i] - 'A') + (k[kindex] - 'A') % 26)) + 'A' ;
printf("%c", crypt ) ;
}
else
{
int crypt = (((s[i] - 'A') + (k[kindex] - 'a')) % 26) + 'A' ;
printf("%c", crypt ) ;
}
}
if(islower(s[i]))
{
if(isupper(k[kindex]))
{
int crypt = (((s[i] - 'a') + (k[kindex] - 'A')) % 26) + 'a' ;
printf("%c", crypt) ;
}
else
{
int crypt = (((s[i] - 'a') + (k[kindex] - 'a')) % 26) + 'a' ;
printf("%c", crypt ) ;
}
}
j++ ;
}
else
{
printf("%c" , s[i]) ;
}
}
printf("\n") ;
return 0 ;
}
With check50, here are the errors I receive :
:( encrypts "BaRFoo" as "CaQGon" using "BaZ" as keyword
\ expected output, but not "CakGon\n"
:( encrypts "BARFOO" as "CAQGON" using "BAZ" as keyword
\ expected output, but not "CAkGOh\n"
Here is my sandbox : sandbox
I don't understand why the two outputs are not the same (cakgon vs cakoh) and why it differs from what is expected. The problem probably resides in the part "//Deliver the crypted test".
I have spent a few hours trying to figure it out without success.
Thanks in advance for any help / tip / piece of advice.
Baptiste
I finally get it.
Parentheses were missing before one of the "%26".

How can I safely and simply read a line of text from a file or stdin?

Given that fgets only sometimes includes a linebreak, and fscanf is inherently unsafe, I would like a simple alternative to read text line-by-line from a file. Is this page a good place to find such a function?
Yes. The following function should satisfy this requirement without creating any damaging security flaws.
/* reads from [stream] into [buffer] until terminated by
* \r, \n or EOF, or [lastnullindex] is reached. Returns
* the number of characters read excluding the terminating
* character. [lastnullindex] refers to the uppermost index
* of the [buffer] array. If an error occurs or non-text
* characters (below space ' ' or above tilde '~') are
* detected, the buffer will be emptied and 0 returned.
*/
int readline(FILE *stream, char *buffer, int lastnullindex) {
if (!stream) return 0;
if (!buffer) return 0;
if (lastnullindex < 0) return 0;
int inch = EOF;
int chi = 0;
while (chi < lastnullindex) {
inch = fgetc(stream);
if (inch == EOF || inch == '\n' || inch == '\r') {
buffer[chi] = '\0';
break;
} else if (inch >= ' ' && inch <= '~') {
buffer[chi] = (char)inch;
chi++;
} else {
buffer[0] = '\0';
return 0;
}
}
if (chi < 0 || chi > lastnullindex) {
buffer[0] = '\0';
return 0;
} else {
buffer[chi] = '\0';
return chi;
}
}

linux terminal output

Hi I wrote a simple c prog to just accept a password while diplaying * to hide the input. But the * for the last character entered is not appearing at the right place.
the code is below
int main(){
int choice = 0;
char pass[8];
FILE *input;
FILE *output;
struct termios initial_settings, new_settings;
if(!isatty(fileno(stdout))){
fprintf(stderr,"Not a terminal \n");
}
input = fopen("/dev/tty","r");
output = fopen("/dev/tty","w");
if(!input || !output){
fprintf(stderr,"error opening");
exit(1);
}
tcgetattr(fileno(input),&initial_settings);
new_settings = initial_settings;
new_settings.c_lflag &= ~ICANON;
new_settings.c_lflag &= ~ECHO;
new_settings.c_cc[VMIN] = 1;
new_settings.c_cc[VTIME] = 0;
new_settings.c_lflag &= ~ISIG;
if(tcsetattr(fileno(input), TCSANOW, &new_settings) != 0) {
fprintf(stderr,"could not set attributes\n");
}
int count = 0;
char ch;
printf("Please enter the password: ");
while (count<8){
ch = fgetc(input);
if(ch == '\n' || ch == '\r'){
break;
}else{
fputc('*',stdout);
pass[count] = ch;
count++;
}
tcdrain(fileno(stdout));
}
fprintf(output,"you have entered :%s \n",pass);
tcsetattr(fileno(input),TCSANOW,&initial_settings);
exit(0);
}
The output is as follows:
Please enter the password:* * * * * * *
you have entered :12345678
* pasman#pasman-laptop:~$
Its an 8 character password & Notice that 7 *s appear as expected but the last * is appearing at the end of main.
You're mixing stdio and another stream, output, talking directly to the tty. They have different buffers, and get flushed at different times. You really should just use one of them.
It's because you break before you write the last *: so
add
fputc('*',stdout);
before
tcdrain(fileno(stdout));

How to check if the given string is palindrome? [closed]

Closed. This question needs to be more focused. It is not currently accepting answers.
Want to improve this question? Update the question so it focuses on one problem only by editing this post.
Closed 7 years ago.
Improve this question
Definition:
A palindrome is a word, phrase, number or other sequence of units that has the property of reading the same in either direction
How to check if the given string is a palindrome?
This was one of the FAIQ [Frequently Asked Interview Question] a while ago but that mostly using C.
Looking for solutions in any and all languages possible.
PHP sample:
$string = "A man, a plan, a canal, Panama";
function is_palindrome($string)
{
$a = strtolower(preg_replace("/[^A-Za-z0-9]/","",$string));
return $a==strrev($a);
}
Removes any non-alphanumeric characters (spaces, commas, exclamation points, etc.) to allow for full sentences as above, as well as simple words.
Windows XP (might also work on 2000) or later BATCH script:
#echo off
call :is_palindrome %1
if %ERRORLEVEL% == 0 (
echo %1 is a palindrome
) else (
echo %1 is NOT a palindrome
)
exit /B 0
:is_palindrome
set word=%~1
set reverse=
call :reverse_chars "%word%"
set return=1
if "$%word%" == "$%reverse%" (
set return=0
)
exit /B %return%
:reverse_chars
set chars=%~1
set reverse=%chars:~0,1%%reverse%
set chars=%chars:~1%
if "$%chars%" == "$" (
exit /B 0
) else (
call :reverse_chars "%chars%"
)
exit /B 0
Language agnostic meta-code then...
rev = StringReverse(originalString)
return ( rev == originalString );
C# in-place algorithm. Any preprocessing, like case insensitivity or stripping of whitespace and punctuation should be done before passing to this function.
boolean IsPalindrome(string s) {
for (int i = 0; i < s.Length / 2; i++)
{
if (s[i] != s[s.Length - 1 - i]) return false;
}
return true;
}
Edit: removed unnecessary "+1" in loop condition and spent the saved comparison on removing the redundant Length comparison. Thanks to the commenters!
C#: LINQ
var str = "a b a";
var test = Enumerable.SequenceEqual(str.ToCharArray(),
str.ToCharArray().Reverse());
A more Ruby-style rewrite of Hal's Ruby version:
class String
def palindrome?
(test = gsub(/[^A-Za-z]/, '').downcase) == test.reverse
end
end
Now you can call palindrome? on any string.
Unoptimized Python:
>>> def is_palindrome(s):
... return s == s[::-1]
Java solution:
public class QuickTest {
public static void main(String[] args) {
check("AmanaplanacanalPanama".toLowerCase());
check("Hello World".toLowerCase());
}
public static void check(String aString) {
System.out.print(aString + ": ");
char[] chars = aString.toCharArray();
for (int i = 0, j = (chars.length - 1); i < (chars.length / 2); i++, j--) {
if (chars[i] != chars[j]) {
System.out.println("Not a palindrome!");
return;
}
}
System.out.println("Found a palindrome!");
}
}
Using a good data structure usually helps impress the professor:
Push half the chars onto a stack (Length / 2).
Pop and compare each char until the first unmatch.
If the stack has zero elements: palindrome.
*in the case of a string with an odd Length, throw out the middle char.
C in the house. (not sure if you didn't want a C example here)
bool IsPalindrome(char *s)
{
int i,d;
int length = strlen(s);
char cf, cb;
for(i=0, d=length-1 ; i < length && d >= 0 ; i++ , d--)
{
while(cf= toupper(s[i]), (cf < 'A' || cf >'Z') && i < length-1)i++;
while(cb= toupper(s[d]), (cb < 'A' || cb >'Z') && d > 0 )d--;
if(cf != cb && cf >= 'A' && cf <= 'Z' && cb >= 'A' && cb <='Z')
return false;
}
return true;
}
That will return true for "racecar", "Racecar", "race car", "racecar ", and "RaCe cAr". It would be easy to modify to include symbols or spaces as well, but I figure it's more useful to only count letters(and ignore case). This works for all palindromes I've found in the answers here, and I've been unable to trick it into false negatives/positives.
Also, if you don't like bool in a "C" program, it could obviously return int, with return 1 and return 0 for true and false respectively.
Here's a python way. Note: this isn't really that "pythonic" but it demonstrates the algorithm.
def IsPalindromeString(n):
myLen = len(n)
i = 0
while i <= myLen/2:
if n[i] != n[myLen-1-i]:
return False
i += 1
return True
Delphi
function IsPalindrome(const s: string): boolean;
var
i, j: integer;
begin
Result := false;
j := Length(s);
for i := 1 to Length(s) div 2 do begin
if s[i] <> s[j] then
Exit;
Dec(j);
end;
Result := true;
end;
I'm seeing a lot of incorrect answers here. Any correct solution needs to ignore whitespace and punctuation (and any non-alphabetic characters actually) and needs to be case insensitive.
A few good example test cases are:
"A man, a plan, a canal, Panama."
"A Toyota's a Toyota."
"A"
""
As well as some non-palindromes.
Example solution in C# (note: empty and null strings are considered palindromes in this design, if this is not desired it's easy to change):
public static bool IsPalindrome(string palindromeCandidate)
{
if (string.IsNullOrEmpty(palindromeCandidate))
{
return true;
}
Regex nonAlphaChars = new Regex("[^a-z0-9]");
string alphaOnlyCandidate = nonAlphaChars.Replace(palindromeCandidate.ToLower(), "");
if (string.IsNullOrEmpty(alphaOnlyCandidate))
{
return true;
}
int leftIndex = 0;
int rightIndex = alphaOnlyCandidate.Length - 1;
while (rightIndex > leftIndex)
{
if (alphaOnlyCandidate[leftIndex] != alphaOnlyCandidate[rightIndex])
{
return false;
}
leftIndex++;
rightIndex--;
}
return true;
}
EDIT: from the comments:
bool palindrome(std::string const& s)
{
return std::equal(s.begin(), s.end(), s.rbegin());
}
The c++ way.
My naive implementation using the elegant iterators. In reality, you would probably check
and stop once your forward iterator has past the halfway mark to your string.
#include <string>
#include <iostream>
using namespace std;
bool palindrome(string foo)
{
string::iterator front;
string::reverse_iterator back;
bool is_palindrome = true;
for(front = foo.begin(), back = foo.rbegin();
is_palindrome && front!= foo.end() && back != foo.rend();
++front, ++back
)
{
if(*front != *back)
is_palindrome = false;
}
return is_palindrome;
}
int main()
{
string a = "hi there", b = "laval";
cout << "String a: \"" << a << "\" is " << ((palindrome(a))? "" : "not ") << "a palindrome." <<endl;
cout << "String b: \"" << b << "\" is " << ((palindrome(b))? "" : "not ") << "a palindrome." <<endl;
}
boolean isPalindrome(String str1) {
//first strip out punctuation and spaces
String stripped = str1.replaceAll("[^a-zA-Z0-9]", "");
return stripped.equalsIgnoreCase((new StringBuilder(stripped)).reverse().toString());
}
Java version
Here's my solution, without using a strrev. Written in C#, but it will work in any language that has a string length function.
private static bool Pal(string s) {
for (int i = 0; i < s.Length; i++) {
if (s[i] != s[s.Length - 1 - i]) {
return false;
}
}
return true;
}
Here's my solution in c#
static bool isPalindrome(string s)
{
string allowedChars = "abcdefghijklmnopqrstuvwxyz"+
"1234567890ABCDEFGHIJKLMNOPQRSTUVWXYZ";
string compareString = String.Empty;
string rev = string.Empty;
for (int i = 0; i <= s.Length - 1; i++)
{
char c = s[i];
if (allowedChars.IndexOf(c) > -1)
{
compareString += c;
}
}
for (int i = compareString.Length - 1; i >= 0; i--)
{
char c = compareString[i];
rev += c;
}
return rev.Equals(compareString,
StringComparison.CurrentCultureIgnoreCase);
}
Here's a Python version that deals with different cases, punctuation and whitespace.
import string
def is_palindrome(palindrome):
letters = palindrome.translate(string.maketrans("",""),
string.whitespace + string.punctuation).lower()
return letters == letters[::-1]
Edit: Shamelessly stole from Blair Conrad's neater answer to remove the slightly clumsy list processing from my previous version.
C++
std::string a = "god";
std::string b = "lol";
std::cout << (std::string(a.rbegin(), a.rend()) == a) << " "
<< (std::string(b.rbegin(), b.rend()) == b);
Bash
function ispalin { [ "$( echo -n $1 | tac -rs . )" = "$1" ]; }
echo "$(ispalin god && echo yes || echo no), $(ispalin lol && echo yes || echo no)"
Gnu Awk
/* obvious solution */
function ispalin(cand, i) {
for(i=0; i<length(cand)/2; i++)
if(substr(cand, length(cand)-i, 1) != substr(cand, i+1, 1))
return 0;
return 1;
}
/* not so obvious solution. cough cough */
{
orig = $0;
while($0) {
stuff = stuff gensub(/^.*(.)$/, "\\1", 1);
$0 = gensub(/^(.*).$/, "\\1", 1);
}
print (stuff == orig);
}
Haskell
Some brain dead way doing it in Haskell
ispalin :: [Char] -> Bool
ispalin a = a == (let xi (y:my) = (xi my) ++ [y]; xi [] = [] in \x -> xi x) a
Plain English
"Just reverse the string and if it is the same as before, it's a palindrome"
Ruby:
class String
def is_palindrome?
letters_only = gsub(/\W/,'').downcase
letters_only == letters_only.reverse
end
end
puts 'abc'.is_palindrome? # => false
puts 'aba'.is_palindrome? # => true
puts "Madam, I'm Adam.".is_palindrome? # => true
An obfuscated C version:
int IsPalindrome (char *s)
{
char*a,*b,c=0;
for(a=b=s;a<=b;c=(c?c==1?c=(*a&~32)-65>25u?*++a,1:2:c==2?(*--b&~32)-65<26u?3:2:c==3?(*b-65&~32)-(*a-65&~32)?*(b=s=0,a),4:*++a,1:0:*++b?0:1));
return s!=0;
}
This Java code should work inside a boolean method:
Note: You only need to check the first half of the characters with the back half, otherwise you are overlapping and doubling the amount of checks that need to be made.
private static boolean doPal(String test) {
for(int i = 0; i < test.length() / 2; i++) {
if(test.charAt(i) != test.charAt(test.length() - 1 - i)) {
return false;
}
}
return true;
}
Another C++ one. Optimized for speed and size.
bool is_palindrome(const std::string& candidate) {
for(std::string::const_iterator left = candidate.begin(), right = candidate.end(); left < --right ; ++left)
if (*left != *right)
return false;
return true;
}
Lisp:
(defun palindrome(x) (string= x (reverse x)))
Three versions in Smalltalk, from dumbest to correct.
In Smalltalk, = is the comparison operator:
isPalindrome: aString
"Dumbest."
^ aString reverse = aString
The message #translateToLowercase returns the string as lowercase:
isPalindrome: aString
"Case insensitive"
|lowercase|
lowercase := aString translateToLowercase.
^ lowercase reverse = lowercase
And in Smalltalk, strings are part of the Collection framework, you can use the message #select:thenCollect:, so here's the last version:
isPalindrome: aString
"Case insensitive and keeping only alphabetic chars
(blanks & punctuation insensitive)."
|lowercaseLetters|
lowercaseLetters := aString
select: [:char | char isAlphabetic]
thenCollect: [:char | char asLowercase].
^ lowercaseLetters reverse = lowercaseLetters
Note that in the above C++ solutions, there was some problems.
One solution was inefficient because it passed an std::string by copy, and because it iterated over all the chars, instead of comparing only half the chars. Then, even when discovering the string was not a palindrome, it continued the loop, waiting its end before reporting "false".
The other was better, with a very small function, whose problem was that it was not able to test anything else than std::string. In C++, it is easy to extend an algorithm to a whole bunch of similar objects. By templating its std::string into "T", it would have worked on both std::string, std::wstring, std::vector and std::deque. But without major modification because of the use of the operator <, the std::list was out of its scope.
My own solutions try to show that a C++ solution won't stop at working on the exact current type, but will strive to work an anything that behaves the same way, no matter the type. For example, I could apply my palindrome tests on std::string, on vector of int or on list of "Anything" as long as Anything was comparable through its operator = (build in types, as well as classes).
Note that the template can even be extended with an optional type that can be used to compare the data. For example, if you want to compare in a case insensitive way, or even compare similar characters (like è, é, ë, ê and e).
Like king Leonidas would have said: "Templates ? This is C++ !!!"
So, in C++, there are at least 3 major ways to do it, each one leading to the other:
Solution A: In a c-like way
The problem is that until C++0X, we can't consider the std::string array of chars as contiguous, so we must "cheat" and retrieve the c_str() property. As we are using it in a read-only fashion, it should be ok...
bool isPalindromeA(const std::string & p_strText)
{
if(p_strText.length() < 2) return true ;
const char * pStart = p_strText.c_str() ;
const char * pEnd = pStart + p_strText.length() - 1 ;
for(; pStart < pEnd; ++pStart, --pEnd)
{
if(*pStart != *pEnd)
{
return false ;
}
}
return true ;
}
Solution B: A more "C++" version
Now, we'll try to apply the same solution, but to any C++ container with random access to its items through operator []. For example, any std::basic_string, std::vector, std::deque, etc. Operator [] is constant access for those containers, so we won't lose undue speed.
template <typename T>
bool isPalindromeB(const T & p_aText)
{
if(p_aText.empty()) return true ;
typename T::size_type iStart = 0 ;
typename T::size_type iEnd = p_aText.size() - 1 ;
for(; iStart < iEnd; ++iStart, --iEnd)
{
if(p_aText[iStart] != p_aText[iEnd])
{
return false ;
}
}
return true ;
}
Solution C: Template powah !
It will work with almost any unordered STL-like container with bidirectional iterators
For example, any std::basic_string, std::vector, std::deque, std::list, etc.
So, this function can be applied on all STL-like containers with the following conditions:
1 - T is a container with bidirectional iterator
2 - T's iterator points to a comparable type (through operator =)
template <typename T>
bool isPalindromeC(const T & p_aText)
{
if(p_aText.empty()) return true ;
typename T::const_iterator pStart = p_aText.begin() ;
typename T::const_iterator pEnd = p_aText.end() ;
--pEnd ;
while(true)
{
if(*pStart != *pEnd)
{
return false ;
}
if((pStart == pEnd) || (++pStart == pEnd))
{
return true ;
}
--pEnd ;
}
}
A simple Java solution:
public boolean isPalindrome(String testString) {
StringBuffer sb = new StringBuffer(testString);
String reverseString = sb.reverse().toString();
if(testString.equalsIgnoreCase(reverseString)) {
return true;
else {
return false;
}
}
Many ways to do it. I guess the key is to do it in the most efficient way possible (without looping the string). I would do it as a char array which can be reversed easily (using C#).
string mystring = "abracadabra";
char[] str = mystring.ToCharArray();
Array.Reverse(str);
string revstring = new string(str);
if (mystring.equals(revstring))
{
Console.WriteLine("String is a Palindrome");
}
In Ruby, converting to lowercase and stripping everything not alphabetic:
def isPalindrome( string )
( test = string.downcase.gsub( /[^a-z]/, '' ) ) == test.reverse
end
But that feels like cheating, right? No pointers or anything! So here's a C version too, but without the lowercase and character stripping goodness:
#include <stdio.h>
int isPalindrome( char * string )
{
char * i = string;
char * p = string;
while ( *++i ); while ( i > p && *p++ == *--i );
return i <= p && *i++ == *--p;
}
int main( int argc, char **argv )
{
if ( argc != 2 )
{
fprintf( stderr, "Usage: %s <word>\n", argv[0] );
return -1;
}
fprintf( stdout, "%s\n", isPalindrome( argv[1] ) ? "yes" : "no" );
return 0;
}
Well, that was fun - do I get the job ;^)
Using Java, using Apache Commons String Utils:
public boolean isPalindrome(String phrase) {
phrase = phrase.toLowerCase().replaceAll("[^a-z]", "");
return StringUtils.reverse(phrase).equals(phrase);
}

Resources