88/* Manufactured in the United States of America. */
99/* */
1010/************************************************************************/
11+ #include <ctype.h> /* for isdigit */
1112#include <errno.h>
1213#include <limits.h> /* for NAME_MAX */
14+ #include <string.h> /* for strlen */
15+ #include <sys/param.h> /* for MAXPATHLEN */
1316#include <dirent.h> /* for MAXNAMLEN */
1417#include "lispemul.h" /* for DLword */
1518
@@ -331,14 +334,10 @@ do { \
331334/*
332335 * Name: UnixVersionToLispVersion
333336 *
334- * Argument: char *pathname
335- * UNIX syntax pathname.
336- * int vlessp
337- * If 0, versionless file is converted to version 1.
337+ * Argument: char *pathname UNIX syntax pathname.
338+ * int vlessp If 0, versionless file is converted to version 1.
338339 * Otherwise, remains as versionless.
339340 *
340- * Value: If succeed, returns 1, otherwise 0.
341- *
342341 * Side Effect: The version part of pathname is destructively modified.
343342 *
344343 * Description:
@@ -353,62 +352,20 @@ do { \
353352 * dealt with as version 1.
354353 */
355354
356- #define UnixVersionToLispVersion (pathname , vlessp ) do { \
357- \
358- char *start; \
359- char *end; \
360- char *lf_cp; \
361- int ver_no; \
362- size_t len; \
363- char ver_buf[VERSIONLEN]; \
364- \
365- if ((start = strchr(pathname, '~')) != NULL) { \
366- /* First of all, find the version field in pathname. */ \
367- end = start ; \
368- lf_cp = start + 1 ; \
369- while (* lf_cp ) { \
370- if (* lf_cp == '~' ) { \
371- start = end ; \
372- end = lf_cp ; \
373- lf_cp ++ ; \
374- } else { \
375- lf_cp ++ ; \
376- } \
377- } \
378- \
379- if (start != end && * (start - 1 ) == '.' && end == (lf_cp - 1 )) { \
380- /* \
381- * pathname ends in the form ".~###~". But we \
382- * check ### is a valid number or not. \
383- */ \
384- len = (end - start ) - 1 ; \
385- strncpy (ver_buf , start + 1 , len ); \
386- ver_buf [len ] = '\0' ; \
387- NumericStringP (ver_buf , YES , NO ); \
388- YES : \
389- * (start - 1 ) = ';' ; \
390- * start = '\0' ; \
391- * end = '\0' ; \
392- /* call strtoul() to eliminate leading 0s. */ \
393- ver_no = strtoul (start + 1 , (char * * )NULL , 10 ); \
394- sprintf (ver_buf , "%u" , ver_no ); \
395- strcat (pathname , ver_buf ); \
396- goto CONT ; \
397- \
398- NO : \
399- /* Dealt with as version 1 unless vlessp */ \
400- if (!(vlessp )) strcat (pathname , ";1" ); \
401- CONT : \
402- lf_cp -- ; /* Just for label */ \
403- } else { \
404- /* Dealt with as version 1 unless vlessp. */ \
405- if (!(vlessp )) strcat (pathname , ";1" ); \
406- } \
407- } else { \
408- /* Dealt with as version 1 unless vlessp. */ \
409- if (!(vlessp )) strcat (pathname , ";1" ); \
410- } \
411- } while (0 )
355+ #define UnixVersionToLispVersion (pathname , vlessp ) \
356+ do { \
357+ char *n_end; \
358+ char *v_start; \
359+ int v_len; \
360+ \
361+ if (!parse_file_version(pathname, 1, &n_end, &v_start, &v_len)) { \
362+ if (!vlessp) strcat(pathname, ";1"); \
363+ } else { \
364+ *n_end++ = ';'; \
365+ while (v_len-- > 0) *n_end++ = *v_start++; \
366+ *n_end = '\0'; \
367+ } \
368+ } while (0)
412369
413370/*
414371 * Name: ConcDirAndName
@@ -481,9 +438,12 @@ do { \
481438 *
482439 * Concatenate the root file name and its version in UNIX format.
483440 *
441+ * XXX: this code is unsafe and could result in memory smashes if the
442+ * sizes of the arguments are not correctly specified
443+ *
484444 */
485445
486- #define ConcNameAndVersion (name , ver , rname ) do { \
446+ #define ConcNameAndVersion (name , ver , rname ) do { \
487447 if (*(ver) != '\0') { \
488448 strcpy(rname, name); \
489449 strcat(rname, ".~"); \
@@ -494,7 +454,7 @@ do { \
494454 } \
495455} while (0)
496456
497- #define VERSIONLEN 16
457+ #define VERSIONLEN 10
498458
499459#define MAXVERSION 999999999
500460
@@ -576,9 +536,9 @@ do { \
576536 TIMEOUT(lf_rval=rename(x, y)); \
577537 if(lf_rval == -1){ \
578538 switch(errno){ \
579- case 2 : \
539+ case ENOENT : \
580540 return(1); \
581- case 18 : \
541+ case EXDEV : \
582542 *Lisp_errno = errno; \
583543 return(0); \
584544 default: \
@@ -601,20 +561,78 @@ do { \
601561/*
602562 * For file name length check
603563 */
604- #define FNAMETOOLONG 200
605564
606- #define FileNameTooLong (val ) do { \
607- *Lisp_errno = FNAMETOOLONG; \
565+ #define FileNameTooLong (val ) do { \
566+ *Lisp_errno = ENAMETOOLONG; \
608567 return((val)); \
609568 } while (0)
610569
611-
612-
613-
614-
615-
616-
617-
570+ static inline int parse_file_version (char * name , int digitsonly , char * * n_end ,
571+ char * * v_start , int * v_length )
572+ {
573+ char * sp , * ep ;
574+ size_t name_len ;
575+
576+ name_len = strlen (name );
577+ ep = & name [name_len - 1 ];
578+
579+ /* handle special case of Alto/IFS names with !nnn version.
580+ To be considered for this case the name MUST end with ![0-9]+, however
581+ version 0 is not valid.
582+ */
583+ sp = strrchr (name , '!' );
584+ if (sp != NULL ) {
585+ sp ++ ; /* "!nnn" => "nnn" or "!" => "" */
586+ if (* sp != '\0' && sp [strspn (sp , "0123456789" )] == '\0' ) {
587+ /* it was all digits after the '!', so go with it */
588+ * n_end = sp - 1 ; /* name ends at '!' */
589+ while (* sp == '0' && sp < ep ) sp ++ ; /* skip leading zeroes */
590+ if (* sp == '0' ) return (0 ); /* version 0 is not valid */
591+ * v_start = sp ; /* version start after '!' */
592+ * v_length = (ep - sp ) + 1 ;
593+ return ((* v_length >= VERSIONLEN ) ? 0 : 1 ); /* fail on version too long */
594+ }
595+ }
596+
597+ /* if the name is too short to have a name and a version number
598+ ".~#~" or doesn't end with "~" then there is no version number
599+ */
600+ if (name_len < 4 || * ep != '~' )
601+ return (0 );
602+
603+ /* The name ends with a "~" so scan back in the filename looking for
604+ another "~" terminating early if we need only digits and find
605+ something else
606+ */
607+ sp = ep - 1 ;
608+ while (sp > name && * sp != '~' ) {
609+ if (digitsonly && !isdigit (* sp )) return (0 );
610+ -- sp ;
611+ }
612+
613+ /* test for no initial "~" or no "." before "~", or
614+ * version number length not at least 1
615+ */
616+ if (sp == name || * (sp - 1 ) != '.' || (ep - sp ) - 1 < 1 )
617+ return (0 );
618+
619+ /* After this point we have a version number in the form .~#~ with sp
620+ pointing at the starting "~", ep pointing at the last "~",
621+ and there must be at least one digit. Scan past any leading
622+ zeros in the version, taking care not to remove the last digit.
623+ */
624+
625+ * n_end = sp - 1 ; /* save location of "." */
626+
627+ sp ++ ; /* skip over the "." */
628+ while (* sp == '0' && sp < (ep - 1 )) {
629+ sp ++ ;
630+ }
631+ if (* sp == '0' ) return (0 ); /* version 0 is not valid */
632+ * v_start = sp ; /* save location of first significant digit in version */
633+ * v_length = (ep - sp ); /* save length of version */
634+ return ((* v_length >= VERSIONLEN ) ? 0 : 1 ); /* fail on version too long */
635+ }
618636
619637/********************************************************/
620638/* file-system-specific defns */
0 commit comments